My-Weekly-ToDo-List/src/lib/export-fields.ts
mARTin-B78 d6de7e8f06
Some checks are pending
Playwright Tests / test (push) Waiting to run
Test and SonarQube / quality (push) Waiting to run
fix: Prisma-Import raus aus dem Client-Bundle der Einstellungen
export-fields.ts importierte prisma auf Modulebene fuer fetchExportData, wurde aber
von der Client-Komponente SettingsSidebar wegen EXPORT_FIELDS mitgezogen -- Oeffnen
der Einstellungen warf "PrismaClient is unable to run in this browser environment".
fetchExportData liegt jetzt in export-data-server.ts, export-fields.ts bleibt frei
von Server-only-Importen.

v1.129.1

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 19:01:02 +02:00

122 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Felddefinitionen und Helfer für den Datenexport.
*
* Lagen bis v1.118.1 in `app/api/user/export/route.ts`. Next.js erlaubt in einer route.ts
* nur Route-Handler als Exporte — alles andere lässt die generierte Typprüfung auflaufen
* ("does not satisfy the constraint"). Ausserdem importierte SettingsSidebar EXPORT_FIELDS
* aus der Route und zog damit einen Server-Handler in den Client-Graphen.
*
* Diese Datei bleibt bewusst frei von Server-only-Importen (prisma, next-auth) — sie wird
* von SettingsSidebar (Client-Komponente) importiert, und jeder Top-Level-Import hier landet
* sonst im Client-Bundle ("PrismaClient is unable to run in this browser environment"),
* selbst wenn nur EXPORT_FIELDS gebraucht wird. Das DB-lesende fetchExportData liegt darum
* in export-data-server.ts.
*/
// ── Field registry ─────────────────────────────────────────────────────────
export type ExportFieldKey =
| 'week'
| 'weekGoal'
| 'completedDate'
| 'scheduledDate'
| 'createdDate'
| 'project'
| 'list'
| 'priority'
| 'url'
| 'notes'
| 'description'
| 'delegatedTo'
| 'delegationNote'
| 'startTime'
| 'kanbanStage';
export const EXPORT_FIELDS: {
key: ExportFieldKey;
labelDe: string;
labelEn: string;
defaultOn: boolean;
csvOnly?: boolean; // not a table column, only used as a section in PDF/CSV
}[] = [
{ key: 'week', labelDe: 'Kalenderwoche', labelEn: 'Calendar week', defaultOn: true, csvOnly: true },
{ key: 'weekGoal', labelDe: 'Wochenziel', labelEn: 'Goal of the week', defaultOn: true, csvOnly: true },
{ key: 'completedDate', labelDe: 'Erledigt am', labelEn: 'Completed', defaultOn: true },
{ key: 'scheduledDate', labelDe: 'Geplant für', labelEn: 'Scheduled for', defaultOn: true },
{ key: 'createdDate', labelDe: 'Erstellt am', labelEn: 'Created', defaultOn: false },
{ key: 'project', labelDe: 'Projekt', labelEn: 'Project', defaultOn: true },
{ key: 'list', labelDe: 'Liste', labelEn: 'List', defaultOn: true },
{ key: 'priority', labelDe: 'Priorität', labelEn: 'Priority', defaultOn: true },
{ key: 'url', labelDe: 'Link / URL', labelEn: 'Link / URL', defaultOn: false },
{ key: 'notes', labelDe: 'Notiz', labelEn: 'Notes', defaultOn: false },
{ key: 'description', labelDe: 'Beschreibung', labelEn: 'Description', defaultOn: false },
{ key: 'delegatedTo', labelDe: 'Delegiert an', labelEn: 'Delegated to', defaultOn: false },
{ key: 'delegationNote', labelDe: 'Delegationsnotiz', labelEn: 'Delegation note', defaultOn: false },
{ key: 'startTime', labelDe: 'Uhrzeit', labelEn: 'Start time', defaultOn: false },
{ key: 'kanbanStage', labelDe: 'Kanban-Stage', labelEn: 'Kanban stage', defaultOn: false },
];
// ── Shared helpers (also used by export-pdf route) ─────────────────────────
export function isoWeek(date: Date): string {
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
const dayNum = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const weekNo = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
return `${d.getUTCFullYear()}-W${String(weekNo).padStart(2, '0')}`;
}
export function weekMondaySunday(weekKey: string): { mon: Date; sun: Date } {
const [year, w] = weekKey.split('-W');
const d = new Date(Date.UTC(parseInt(year), 0, 1));
d.setUTCDate(d.getUTCDate() + (parseInt(w) - 1) * 7 - (d.getUTCDay() || 7) + 1);
const sun = new Date(d); sun.setUTCDate(d.getUTCDate() + 6);
return { mon: d, sun };
}
export function weekLabel(weekKey: string, de: boolean): string {
const [, w] = weekKey.split('-W');
const year = weekKey.split('-W')[0];
const { mon, sun } = weekMondaySunday(weekKey);
const fmt = (x: Date) => x.toLocaleDateString(de ? 'de-DE' : 'en-GB', { day: '2-digit', month: 'short', timeZone: 'UTC' });
return de
? `KW ${w} — ${year} (${fmt(mon)} – ${fmt(sun)})`
: `Week ${w} — ${year} (${fmt(mon)} – ${fmt(sun)})`;
}
export function fmtDate(d: Date | null | undefined, de: boolean): string {
if (!d) return '';
return de
? d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })
: d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
}
export function priorityLabel(task: any, de: boolean): string {
if (task.urgency != null && task.importance != null) {
if (task.urgency && task.importance) return de ? 'Sofort erledigen' : 'Do now';
if (!task.urgency && task.importance) return de ? 'Planen' : 'Schedule';
if (task.urgency && !task.importance) return de ? 'Delegieren' : 'Delegate';
return de ? 'Eliminieren' : 'Eliminate';
}
if (task.priority) return task.priority;
return '';
}
export function priorityColor(task: any): string {
if (task.urgency != null && task.importance != null) {
if (task.urgency && task.importance) return '#ef4444';
if (!task.urgency && task.importance) return '#3b82f6';
if (task.urgency && !task.importance) return '#f97316';
return '#9ca3af';
}
const map: Record<string, string> = { A: '#ef4444', B: '#f97316', C: '#eab308', D: '#3b82f6', E: '#9ca3af' };
return task.priority ? (map[task.priority] || '#6b7280') : '#6b7280';
}
export function csvCell(value: string): string {
return `"${(value ?? '').replace(/"/g, '""').replace(/\n/g, ' ')}"`;
}
// ── CSV route ──────────────────────────────────────────────────────────────