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>
This commit is contained in:
parent
64f0eb73f8
commit
d6de7e8f06
19
CHANGELOG.md
19
CHANGELOG.md
@ -2,6 +2,25 @@
|
|||||||
|
|
||||||
All notable changes to this project are documented in this file.
|
All notable changes to this project are documented in this file.
|
||||||
|
|
||||||
|
## [1.129.1] - 2026-09-14
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **"PrismaClient is unable to run in this browser environment"** beim Öffnen der
|
||||||
|
Einstellungen (Zahnrad-Icon). `src/lib/export-fields.ts` importierte `prisma` auf
|
||||||
|
Modulebene für das dortige `fetchExportData`, wurde aber von `SettingsSidebar.tsx`
|
||||||
|
(Client-Komponente) wegen `EXPORT_FIELDS` mitimportiert — der komplette Import-Graph
|
||||||
|
landete dadurch im Client-Bundle. `fetchExportData` (und damit der Prisma-Import) ist
|
||||||
|
jetzt in `src/lib/export-data-server.ts` ausgelagert; `export-fields.ts` enthält nur
|
||||||
|
noch reine, client-sichere Helfer und Konstanten.
|
||||||
|
- Zusätzlicher Produktionsvorfall, unabhängig von diesem Bug: der v1.129.0-Build war
|
||||||
|
durch einen externen Container-Reboot mitten im Deploy-Zyklus beschädigt
|
||||||
|
(`.next/standalone/` unvollständig) und wurde per Rollback auf v1.128.0 wieder
|
||||||
|
hergestellt, bevor dieser Fix gebaut und ausgerollt wurde — daher fehlte die
|
||||||
|
"Überfällig"-Übersicht zwischenzeitlich komplett aus der Oberfläche.
|
||||||
|
|
||||||
|
Verifiziert: Typecheck und Produktions-Build (Scratchpad-Klon, da `/home/sparky/Projekte`
|
||||||
|
auf CIFS liegt) grün, `grep PrismaClient` über alle Client-Chunks von `/tasks` leer.
|
||||||
|
|
||||||
## [1.129.0] - 2026-09-14
|
## [1.129.0] - 2026-09-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.129.0",
|
"version": "1.129.1",
|
||||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -8,8 +8,9 @@ import {
|
|||||||
} from '@react-pdf/renderer';
|
} from '@react-pdf/renderer';
|
||||||
import {
|
import {
|
||||||
EXPORT_FIELDS, ExportFieldKey,
|
EXPORT_FIELDS, ExportFieldKey,
|
||||||
fetchExportData, weekLabel, fmtDate, priorityLabel, priorityColor,
|
weekLabel, fmtDate, priorityLabel, priorityColor,
|
||||||
} from '@/lib/export-fields';
|
} from '@/lib/export-fields';
|
||||||
|
import { fetchExportData } from '@/lib/export-data-server';
|
||||||
|
|
||||||
// ── PDF styles ─────────────────────────────────────────────────────────────
|
// ── PDF styles ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import { NextResponse } from 'next/server';
|
|||||||
import { getServerSession } from 'next-auth';
|
import { getServerSession } from 'next-auth';
|
||||||
import { authOptions } from "@/lib/auth";
|
import { authOptions } from "@/lib/auth";
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import { EXPORT_FIELDS, fetchExportData, weekLabel, fmtDate, priorityLabel, csvCell } from '@/lib/export-fields';
|
import { EXPORT_FIELDS, weekLabel, fmtDate, priorityLabel, csvCell } from '@/lib/export-fields';
|
||||||
import type { ExportFieldKey } from '@/lib/export-fields';
|
import type { ExportFieldKey } from '@/lib/export-fields';
|
||||||
|
import { fetchExportData } from '@/lib/export-data-server';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
|
|||||||
58
src/lib/export-data-server.ts
Normal file
58
src/lib/export-data-server.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Server-only Datenbeschaffung für den Export (CSV/PDF-Routen).
|
||||||
|
*
|
||||||
|
* Ausgelagert aus export-fields.ts, weil SettingsSidebar (Client-Komponente) von dort
|
||||||
|
* EXPORT_FIELDS importiert — jeder Top-Level-Import in dieser Datei (prisma) wäre sonst
|
||||||
|
* ins Client-Bundle gewandert ("PrismaClient is unable to run in this browser environment").
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { prisma } from '@/lib/prisma';
|
||||||
|
import { isoWeek } from '@/lib/export-fields';
|
||||||
|
|
||||||
|
const DEFAULT_GOAL = 'your goal of this week';
|
||||||
|
|
||||||
|
export async function fetchExportData(userId: string, startDate: string | null, endDate: string | null) {
|
||||||
|
const where: any = { userId, completed: true, deletedAt: null };
|
||||||
|
if (startDate || endDate) {
|
||||||
|
where.updatedAt = {};
|
||||||
|
if (startDate) where.updatedAt.gte = new Date(startDate);
|
||||||
|
if (endDate) {
|
||||||
|
const end = new Date(endDate);
|
||||||
|
end.setHours(23, 59, 59, 999);
|
||||||
|
where.updatedAt.lte = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [tasks, goals] = await Promise.all([
|
||||||
|
prisma.task.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: { updatedAt: 'desc' },
|
||||||
|
include: {
|
||||||
|
project: { select: { name: true, color: true } },
|
||||||
|
somedayList: { select: { title: true } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
prisma.weeklyGoal.findMany({
|
||||||
|
where: { userId },
|
||||||
|
select: { weekStart: true, text: true },
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Build week → goal map (weekStart is always Monday)
|
||||||
|
const goalByWeek = new Map<string, string>();
|
||||||
|
for (const g of goals) {
|
||||||
|
if (g.text && g.text !== DEFAULT_GOAL) {
|
||||||
|
goalByWeek.set(isoWeek(g.weekStart), g.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group tasks by ISO week
|
||||||
|
const byWeek = new Map<string, typeof tasks>();
|
||||||
|
for (const task of tasks) {
|
||||||
|
const key = isoWeek(task.updatedAt);
|
||||||
|
if (!byWeek.has(key)) byWeek.set(key, []);
|
||||||
|
byWeek.get(key)!.push(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { byWeek, goalByWeek };
|
||||||
|
}
|
||||||
@ -5,13 +5,14 @@
|
|||||||
* nur Route-Handler als Exporte — alles andere lässt die generierte Typprüfung auflaufen
|
* nur Route-Handler als Exporte — alles andere lässt die generierte Typprüfung auflaufen
|
||||||
* ("does not satisfy the constraint"). Ausserdem importierte SettingsSidebar EXPORT_FIELDS
|
* ("does not satisfy the constraint"). Ausserdem importierte SettingsSidebar EXPORT_FIELDS
|
||||||
* aus der Route und zog damit einen Server-Handler in den Client-Graphen.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NextResponse } from 'next/server';
|
|
||||||
import { getServerSession } from 'next-auth';
|
|
||||||
import { authOptions } from "@/lib/auth";
|
|
||||||
import { prisma } from '@/lib/prisma';
|
|
||||||
|
|
||||||
// ── Field registry ─────────────────────────────────────────────────────────
|
// ── Field registry ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export type ExportFieldKey =
|
export type ExportFieldKey =
|
||||||
@ -117,54 +118,4 @@ export function csvCell(value: string): string {
|
|||||||
return `"${(value ?? '').replace(/"/g, '""').replace(/\n/g, ' ')}"`;
|
return `"${(value ?? '').replace(/"/g, '""').replace(/\n/g, ' ')}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_GOAL = 'your goal of this week';
|
|
||||||
|
|
||||||
// ── Fetch shared export data (also used by PDF route) ──────────────────────
|
|
||||||
|
|
||||||
export async function fetchExportData(userId: string, startDate: string | null, endDate: string | null) {
|
|
||||||
const where: any = { userId, completed: true, deletedAt: null };
|
|
||||||
if (startDate || endDate) {
|
|
||||||
where.updatedAt = {};
|
|
||||||
if (startDate) where.updatedAt.gte = new Date(startDate);
|
|
||||||
if (endDate) {
|
|
||||||
const end = new Date(endDate);
|
|
||||||
end.setHours(23, 59, 59, 999);
|
|
||||||
where.updatedAt.lte = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [tasks, goals] = await Promise.all([
|
|
||||||
prisma.task.findMany({
|
|
||||||
where,
|
|
||||||
orderBy: { updatedAt: 'desc' },
|
|
||||||
include: {
|
|
||||||
project: { select: { name: true, color: true } },
|
|
||||||
somedayList: { select: { title: true } },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.weeklyGoal.findMany({
|
|
||||||
where: { userId },
|
|
||||||
select: { weekStart: true, text: true },
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Build week → goal map (weekStart is always Monday)
|
|
||||||
const goalByWeek = new Map<string, string>();
|
|
||||||
for (const g of goals) {
|
|
||||||
if (g.text && g.text !== DEFAULT_GOAL) {
|
|
||||||
goalByWeek.set(isoWeek(g.weekStart), g.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group tasks by ISO week
|
|
||||||
const byWeek = new Map<string, typeof tasks>();
|
|
||||||
for (const task of tasks) {
|
|
||||||
const key = isoWeek(task.updatedAt);
|
|
||||||
if (!byWeek.has(key)) byWeek.set(key, []);
|
|
||||||
byWeek.get(key)!.push(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { byWeek, goalByWeek };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── CSV route ──────────────────────────────────────────────────────────────
|
// ── CSV route ──────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user