fix: Prisma-Import raus aus dem Client-Bundle der Einstellungen
Some checks are pending
Playwright Tests / test (push) Waiting to run
Test and SonarQube / quality (push) Waiting to run

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:
mARTin-B78 2026-09-14 19:00:51 +02:00
parent 64f0eb73f8
commit d6de7e8f06
6 changed files with 88 additions and 58 deletions

View File

@ -2,6 +2,25 @@
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
### Added

View File

@ -1,6 +1,6 @@
{
"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",
"main": "index.js",
"scripts": {

View File

@ -8,8 +8,9 @@ import {
} from '@react-pdf/renderer';
import {
EXPORT_FIELDS, ExportFieldKey,
fetchExportData, weekLabel, fmtDate, priorityLabel, priorityColor,
weekLabel, fmtDate, priorityLabel, priorityColor,
} from '@/lib/export-fields';
import { fetchExportData } from '@/lib/export-data-server';
// ── PDF styles ─────────────────────────────────────────────────────────────

View File

@ -2,8 +2,9 @@ import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from "@/lib/auth";
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 { fetchExportData } from '@/lib/export-data-server';
export async function GET(request: Request) {
const session = await getServerSession(authOptions);

View 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 };
}

View File

@ -5,13 +5,14 @@
* 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.
*/
import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from "@/lib/auth";
import { prisma } from '@/lib/prisma';
// ── Field registry ─────────────────────────────────────────────────────────
export type ExportFieldKey =
@ -117,54 +118,4 @@ export function csvCell(value: string): string {
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 ──────────────────────────────────────────────────────────────