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.
|
||||
|
||||
## [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
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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 ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
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
|
||||
* ("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 ──────────────────────────────────────────────────────────────
|
||||
|
||||
Loading…
Reference in New Issue
Block a user