From d33a5d6c63ac37959b414594070038164f1e526a Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sat, 3 Oct 2026 18:59:47 +0200 Subject: [PATCH] chore: enable TypeScript strict mode and drop dead Payload scripts The shared base tsconfig had strict off, so null-safety and union narrowing were not checked. Strict is now on for apps/web with 5 small typing fixes. check-db.ts and migrate-drafts.js depended on Payload, which is no longer a dependency, and are removed. CLAUDE.md no longer claims Payload. Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 2 +- apps/web/app/(site)/blog/[slug]/page.tsx | 4 +- apps/web/check-db.ts | 12 - apps/web/scripts/check-forms.ts | 6 +- apps/web/src/components/Mermaid.tsx | 92 ++- .../simulations/ArchitectureBuilder.tsx | 510 ++++++++++------ .../simulations/DigitalAssetVisualizer.tsx | 548 ++++++++++++------ apps/web/tsconfig.json | 1 + migrate-drafts.js | 34 -- 9 files changed, 774 insertions(+), 435 deletions(-) delete mode 100644 apps/web/check-db.ts delete mode 100644 migrate-drafts.js diff --git a/CLAUDE.md b/CLAUDE.md index 8e861f6..5cb4e22 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # mintel.me -Personal website of a senior freelance engineer. Next.js 16 (App Router) + Payload, pnpm monorepo. +Personal website of a senior freelance engineer. Next.js 16 (App Router), pnpm monorepo. App lives in `apps/web`. Concept and scope: `apps/web/plans/freelance-relaunch.md` (read it first). ## Commands (run from repo root) diff --git a/apps/web/app/(site)/blog/[slug]/page.tsx b/apps/web/app/(site)/blog/[slug]/page.tsx index 09c4cce..ebdbc67 100644 --- a/apps/web/app/(site)/blog/[slug]/page.tsx +++ b/apps/web/app/(site)/blog/[slug]/page.tsx @@ -93,7 +93,7 @@ export default async function BlogPostPage({ {post.tags && post.tags.length > 0 && (
- {post.tags?.map((tag) => ( + {post.tags?.map((tag: string) => ( - +
diff --git a/apps/web/check-db.ts b/apps/web/check-db.ts deleted file mode 100644 index 9b8998c..0000000 --- a/apps/web/check-db.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const run = async ({ payload }) => { - const docs = await payload.find({ - collection: "context-files", - limit: 100, - }); - console.log(`--- DB CHECK ---`); - console.log(`Found ${docs.totalDocs} context files.`); - docs.docs.forEach((doc) => { - console.log(`- ${doc.filename}`); - }); - process.exit(0); -}; diff --git a/apps/web/scripts/check-forms.ts b/apps/web/scripts/check-forms.ts index b9d4767..8b2aaa5 100644 --- a/apps/web/scripts/check-forms.ts +++ b/apps/web/scripts/check-forms.ts @@ -81,9 +81,11 @@ async function main() { } }); - page.on("pageerror", (err: Error) => { + page.on("pageerror", (err: unknown) => { if (currentScannedUrl.includes("showcase")) return; - console.error(` [PAGE EXCEPTION] ${err.message}`); + console.error( + ` [PAGE EXCEPTION] ${err instanceof Error ? err.message : String(err)}`, + ); }); // Listen to ALL network responses to catch broken assets (404/500) diff --git a/apps/web/src/components/Mermaid.tsx b/apps/web/src/components/Mermaid.tsx index 0a07a5e..f9d3320 100644 --- a/apps/web/src/components/Mermaid.tsx +++ b/apps/web/src/components/Mermaid.tsx @@ -22,7 +22,10 @@ interface MermaidProps { legendFontSize?: string; } -class MermaidErrorBoundary extends React.Component<{ children: React.ReactNode }, { hasError: boolean }> { +class MermaidErrorBoundary extends React.Component< + { children: React.ReactNode }, + { hasError: boolean } +> { constructor(props: { children: React.ReactNode }) { super(props); this.state = { hasError: false }; @@ -73,16 +76,17 @@ const MermaidInternal: React.FC = ({ // Extract text from React children nodes (MDX parses multi-line content as React nodes) const extractTextFromChildren = (node: React.ReactNode): string => { - if (typeof node === 'string') return node; - if (typeof node === 'number') return String(node); - if (Array.isArray(node)) return node.map(extractTextFromChildren).join('\n'); + if (typeof node === "string") return node; + if (typeof node === "number") return String(node); + if (Array.isArray(node)) + return node.map(extractTextFromChildren).join("\n"); if (React.isValidElement(node)) { const props = node.props as { children?: React.ReactNode }; if (props.children) { return extractTextFromChildren(props.children); } } - return ''; + return ""; }; const rawGraph = graph || extractTextFromChildren(children) || ""; @@ -91,8 +95,8 @@ const MermaidInternal: React.FC = ({ // We need to unescape these back to real newlines for Mermaid to parse const sanitizedGraph = rawGraph .trim() - .replace(/^`+|`+$/g, '') - .replace(/\\n/g, '\n') + .replace(/^`+|`+$/g, "") + .replace(/\\n/g, "\n") .replace(/\\"/g, '"') .replace(/\\'/g, "'"); @@ -100,13 +104,16 @@ const MermaidInternal: React.FC = ({ if (!sanitizedGraph || sanitizedGraph.length < 5) { return (
-

Diagramm konnte nicht geladen werden

+

+ Diagramm konnte nicht geladen werden +

); } useEffect(() => { - const generatedId = providedId || `mermaid-${Math.random().toString(36).substring(2, 11)}`; + const generatedId = + providedId || `mermaid-${Math.random().toString(36).substring(2, 11)}`; setId(generatedId); }, [providedId]); @@ -123,7 +130,7 @@ const MermaidInternal: React.FC = ({ } }); }, - { rootMargin: "200px" } // Start rendering 200px before it scrolls into view + { rootMargin: "200px" }, // Start rendering 200px before it scrolls into view ); observer.observe(wrapperRef.current); @@ -143,17 +150,17 @@ const MermaidInternal: React.FC = ({ flowchart: { useMaxWidth: true, htmlLabels: false, - curve: 'basis', + curve: "basis", nodeSpacing: 50, rankSpacing: 60, - padding: 15 + padding: 15, }, sequence: { useMaxWidth: true, showSequenceNumbers: false, height: 40, actorMargin: 30, - messageMargin: 30 + messageMargin: 30, }, gantt: { useMaxWidth: true }, pie: { useMaxWidth: true }, @@ -175,7 +182,8 @@ const MermaidInternal: React.FC = ({ clusterBkg: "#f8fafc", clusterBorder: "#cbd5e1", edgeLabelBackground: "#ffffff", - fontFamily: "var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", + fontFamily: + "var(--font-geist-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", fontSize: fontSize || "18px", nodeFontSize: nodeFontSize || fontSize || "18px", labelFontSize: labelFontSize || fontSize || "16px", @@ -216,15 +224,19 @@ const MermaidInternal: React.FC = ({ if (!mermaid.render) return; const uniqueSvgId = `${id}-svg-${Date.now()}`; - const tempDiv = document.createElement('div'); + const tempDiv = document.createElement("div"); document.body.appendChild(tempDiv); - tempDiv.style.position = 'absolute'; - tempDiv.style.left = '-9999px'; - tempDiv.style.visibility = 'hidden'; + tempDiv.style.position = "absolute"; + tempDiv.style.left = "-9999px"; + tempDiv.style.visibility = "hidden"; let rawSvg: string; try { - const result = await mermaid.render(uniqueSvgId, sanitizedGraph, tempDiv); + const result = await mermaid.render( + uniqueSvgId, + sanitizedGraph, + tempDiv, + ); rawSvg = result.svg; } finally { if (document.body.contains(tempDiv)) { @@ -233,11 +245,13 @@ const MermaidInternal: React.FC = ({ } let scaledSvg = rawSvg; - const svgStartEnd = rawSvg.indexOf('>'); + const svgStartEnd = rawSvg.indexOf(">"); if (svgStartEnd > -1) { const svgTag = rawSvg.substring(0, svgStartEnd + 1); const rest = rawSvg.substring(svgStartEnd + 1); - const newSvgTag = svgTag.replace(/width="[^"]*"/, 'width="100%"').replace(/height="[^"]*"/, 'height="auto"'); + const newSvgTag = svgTag + .replace(/width="[^"]*"/, 'width="100%"') + .replace(/height="[^"]*"/, 'height="auto"'); scaledSvg = newSvgTag + rest; } @@ -250,7 +264,7 @@ const MermaidInternal: React.FC = ({ setError("Diagramm konnte nicht geladen werden (Render-Fehler)."); setIsRendered(true); } else { - await new Promise(resolve => setTimeout(resolve, attempt * 200)); + await new Promise((resolve) => setTimeout(resolve, attempt * 200)); } } } @@ -265,7 +279,7 @@ const MermaidInternal: React.FC = ({ } }); - resizeObserver.observe(wrapperRef.current); + if (wrapperRef.current) resizeObserver.observe(wrapperRef.current); if (wrapperRef.current && wrapperRef.current.clientWidth > 0) { renderGraph(); @@ -273,25 +287,41 @@ const MermaidInternal: React.FC = ({ } return () => resizeObserver.disconnect(); - }, [isVisible, id, sanitizedGraph, fontSize, nodeFontSize, labelFontSize, actorFontSize, messageFontSize, noteFontSize, titleFontSize, sectionFontSize, legendFontSize]); + }, [ + isVisible, + id, + sanitizedGraph, + fontSize, + nodeFontSize, + labelFontSize, + actorFontSize, + messageFontSize, + noteFontSize, + titleFontSize, + sectionFontSize, + legendFontSize, + ]); if (!id) return null; return ( -
- +
-
- {showShare && (
- +
)} @@ -322,7 +352,7 @@ const MermaidInternal: React.FC = ({ id={id} style={{ maxWidth: "100%", - overflow: "visible" + overflow: "visible", }} > {error ? ( @@ -332,7 +362,7 @@ const MermaidInternal: React.FC = ({ ) : svgContent ? (
) : ( -
{sanitizedGraph}
+
{sanitizedGraph}
)}
diff --git a/apps/web/src/components/simulations/ArchitectureBuilder.tsx b/apps/web/src/components/simulations/ArchitectureBuilder.tsx index ea19769..ed377b0 100644 --- a/apps/web/src/components/simulations/ArchitectureBuilder.tsx +++ b/apps/web/src/components/simulations/ArchitectureBuilder.tsx @@ -1,175 +1,357 @@ "use client"; -import React, { useState } from 'react'; -import { ComponentShareButton } from '../ComponentShareButton'; -import { Reveal } from '../Reveal'; -import { ComposedChart, Line, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; -import { ShieldAlert, Rocket, Database, Lock } from 'lucide-react'; +import React, { useState } from "react"; +import { ComponentShareButton } from "../ComponentShareButton"; +import { Reveal } from "../Reveal"; +import { + ComposedChart, + Line, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, +} from "recharts"; +import { ShieldAlert, Rocket, Database, Lock } from "lucide-react"; const archData = [ - { year: 'Jahr 1', saasInvest: 50, saasIndep: 40, customInvest: 120, customIndep: 80 }, - { year: 'Jahr 2', saasInvest: 100, saasIndep: 30, customInvest: 140, customIndep: 90 }, - { year: 'Jahr 3', saasInvest: 155, saasIndep: 20, customInvest: 155, customIndep: 95 }, - { year: 'Jahr 4', saasInvest: 220, saasIndep: 10, customInvest: 170, customIndep: 100 }, - { year: 'Jahr 5', saasInvest: 290, saasIndep: 5, customInvest: 185, customIndep: 100 }, + { + year: "Jahr 1", + saasInvest: 50, + saasIndep: 40, + customInvest: 120, + customIndep: 80, + }, + { + year: "Jahr 2", + saasInvest: 100, + saasIndep: 30, + customInvest: 140, + customIndep: 90, + }, + { + year: "Jahr 3", + saasInvest: 155, + saasIndep: 20, + customInvest: 155, + customIndep: 95, + }, + { + year: "Jahr 4", + saasInvest: 220, + saasIndep: 10, + customInvest: 170, + customIndep: 100, + }, + { + year: "Jahr 5", + saasInvest: 290, + saasIndep: 5, + customInvest: 185, + customIndep: 100, + }, ]; -export function ArchitectureBuilder({ className = '' }: { className?: string }) { - const [stack, setStack] = useState<'saas' | 'custom'>('custom'); +export function ArchitectureBuilder({ + className = "", +}: { + className?: string; +}) { + const [stack, setStack] = useState<"saas" | "custom">("custom"); - return ( - -
-
+ return ( + +
+
-
-
- -
+
+
+ +
-
-
-
-
- 🏗️ -

Zeitlicher Verlauf: Investment vs. Unabhängigkeit

-
-

- Wie Plattform-Abhängigkeit (Lock-in) über die Jahre Ihre Skalierbarkeit zerstört, während ein Headless-System echte Souveränität aufbaut. -

-
-
- -
- {/* Control Tabs */} -
- - -
- - {/* Chart Area */} -
-
- - - - - - - - - - - - - - - - - - {/* Left Y-Axis: Cost */} - `€${val}k`} - axisLine={false} - tickLine={false} - tick={{ fill: '#94a3b8', fontSize: 12 }} - dx={-10} - /> - - {/* Right Y-Axis: Independence */} - `${val}%`} - axisLine={false} - tickLine={false} - tick={{ fill: '#14b8a6', fontSize: 12, fontWeight: 'bold' }} - dx={10} - domain={[0, 100]} - /> - - { - if (name === 'saasInvest') return [`€${value}.000`, 'Kumuliertes Investment (Miete + Addons)']; - if (name === 'customInvest') return [`€${value}.000`, 'Kumuliertes Investment (Bau + Betrieb)']; - if (name === 'saasIndep') return [`${value}%`, 'Technische Unabhängigkeit (Lock-in sinkt)']; - if (name === 'customIndep') return [`${value}%`, 'Technische Unabhängigkeit & Datenhoheit']; - return [value, name]; - }} - labelStyle={{ fontWeight: 'bold', color: '#0f172a', marginBottom: '8px' }} - /> - - {stack === 'saas' ? ( - <> - - - - ) : ( - <> - - - - )} - - -
- - {/* Impact Summary */} -
-
-
- {stack === 'saas' ? : } -
-
-
- {stack === 'saas' ? 'Massiver Platform Lock-in' : '100% Datenhoheit'} -
-
- {stack === 'saas' - ? 'Ihre Daten und Prozesse liegen bei Dritten. Umzüge werden nach Jahr 2 exponentiell teuers und riskant.' - : 'Sie besitzen die Kern-Datenbanken und APIs. Keine Lizenzschocks, keine Blackbox-Updates.'} -
-
-
-
-
-
-
+
+
+
+
+ 🏗️ +

+ Zeitlicher Verlauf: Investment vs. Unabhängigkeit +

+
+

+ Wie Plattform-Abhängigkeit (Lock-in) über die Jahre Ihre + Skalierbarkeit zerstört, während ein Headless-System echte + Souveränität aufbaut. +

- - ); +
+ +
+ {/* Control Tabs */} +
+ + +
+ + {/* Chart Area */} +
+
+ + + + + + + + + + + + + + + + + + {/* Left Y-Axis: Cost */} + `€${val}k`} + axisLine={false} + tickLine={false} + tick={{ fill: "#94a3b8", fontSize: 12 }} + dx={-10} + /> + + {/* Right Y-Axis: Independence */} + `${val}%`} + axisLine={false} + tickLine={false} + tick={{ + fill: "#14b8a6", + fontSize: 12, + fontWeight: "bold", + }} + dx={10} + domain={[0, 100]} + /> + + { + if (name === "saasInvest") + return [ + `€${value}.000`, + "Kumuliertes Investment (Miete + Addons)", + ]; + if (name === "customInvest") + return [ + `€${value}.000`, + "Kumuliertes Investment (Bau + Betrieb)", + ]; + if (name === "saasIndep") + return [ + `${value}%`, + "Technische Unabhängigkeit (Lock-in sinkt)", + ]; + if (name === "customIndep") + return [ + `${value}%`, + "Technische Unabhängigkeit & Datenhoheit", + ]; + return [String(value), String(name)]; + }} + labelStyle={{ + fontWeight: "bold", + color: "#0f172a", + marginBottom: "8px", + }} + /> + + {stack === "saas" ? ( + <> + + + + ) : ( + <> + + + + )} + + +
+ + {/* Impact Summary */} +
+
+
+ {stack === "saas" ? ( + + ) : ( + + )} +
+
+
+ {stack === "saas" + ? "Massiver Platform Lock-in" + : "100% Datenhoheit"} +
+
+ {stack === "saas" + ? "Ihre Daten und Prozesse liegen bei Dritten. Umzüge werden nach Jahr 2 exponentiell teuers und riskant." + : "Sie besitzen die Kern-Datenbanken und APIs. Keine Lizenzschocks, keine Blackbox-Updates."} +
+
+
+
+
+
+
+
+ + ); } diff --git a/apps/web/src/components/simulations/DigitalAssetVisualizer.tsx b/apps/web/src/components/simulations/DigitalAssetVisualizer.tsx index a7c2b8b..d2d1a78 100644 --- a/apps/web/src/components/simulations/DigitalAssetVisualizer.tsx +++ b/apps/web/src/components/simulations/DigitalAssetVisualizer.tsx @@ -1,201 +1,371 @@ "use client"; -import React, { useState } from 'react'; -import { ComponentShareButton } from '../ComponentShareButton'; -import { Reveal } from '../Reveal'; -import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; -import { Briefcase, Coins, PiggyBank, TrendingUp } from 'lucide-react'; +import React, { useState } from "react"; +import { ComponentShareButton } from "../ComponentShareButton"; +import { Reveal } from "../Reveal"; +import { + Area, + AreaChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; +import { Briefcase, Coins, PiggyBank, TrendingUp } from "lucide-react"; const data = [ - { year: 'Jahr 1', saasCost: 40000, saasAsset: 0, customCost: 120000, customAsset: 120000 }, - { year: 'Jahr 2', saasCost: 85000, saasAsset: 0, customCost: 140000, customAsset: 110000 }, - { year: 'Jahr 3', saasCost: 135000, saasAsset: 0, customCost: 160000, customAsset: 100000 }, - { year: 'Jahr 4', saasCost: 190000, saasAsset: 0, customCost: 180000, customAsset: 90000 }, - { year: 'Jahr 5', saasCost: 250000, saasAsset: 0, customCost: 200000, customAsset: 85000 }, + { + year: "Jahr 1", + saasCost: 40000, + saasAsset: 0, + customCost: 120000, + customAsset: 120000, + }, + { + year: "Jahr 2", + saasCost: 85000, + saasAsset: 0, + customCost: 140000, + customAsset: 110000, + }, + { + year: "Jahr 3", + saasCost: 135000, + saasAsset: 0, + customCost: 160000, + customAsset: 100000, + }, + { + year: "Jahr 4", + saasCost: 190000, + saasAsset: 0, + customCost: 180000, + customAsset: 90000, + }, + { + year: "Jahr 5", + saasCost: 250000, + saasAsset: 0, + customCost: 200000, + customAsset: 85000, + }, ]; -const formatCurrency = (val: number) => new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(val); +const formatCurrency = (val: number) => + new Intl.NumberFormat("de-DE", { + style: "currency", + currency: "EUR", + maximumFractionDigits: 0, + }).format(val); -export function DigitalAssetVisualizer({ className = '' }: { className?: string }) { - const [view, setView] = useState<'saas' | 'custom'>('custom'); +export function DigitalAssetVisualizer({ + className = "", +}: { + className?: string; +}) { + const [view, setView] = useState<"saas" | "custom">("custom"); - return ( - -
-
+ return ( + +
+
-
-
- -
+
+
+ +
-
-
-
-
- 💰 -

Software: Ausgabe oder Investment?

-
-

- Kumulierte Kosten im Vergleich zum bilanzierbaren Unternehmenswert über 5 Jahre. -

-
-
- -
- {/* Control Tabs */} -
- - -
- - {/* Chart Area */} -
-
- - - - - - - - - - - - - - - - - - - `€${val / 1000}k`} - axisLine={false} - tickLine={false} - tick={{ fill: '#64748b', fontSize: 12 }} - dx={-10} - /> - { - if (name === 'saasCost') return [formatCurrency(value), 'Kumulierte Kosten (Miete)']; - if (name === 'customCost') return [formatCurrency(value), 'Kumulierte Kosten (Bau + Wartung)']; - if (name === 'customAsset') return [formatCurrency(value), 'Firmenwert (Digitales Eigentum)']; - if (name === 'saasAsset') return [formatCurrency(value), 'Firmenwert (IP)']; - return [value, name]; - }} - labelStyle={{ fontWeight: 'bold', color: '#0f172a', marginBottom: '4px', fontSize: '11px' }} - /> - - {view === 'saas' ? ( - <> - - - - ) : ( - <> - - - - )} - - -
- - {/* Summary Metrics */} -
-
-
- Total Spend (Jahr 5) -
-
- {view === 'saas' ? '250.000 €' : '200.000 €'} - {view === 'saas' && } -
-
Sunk Costs (Geldabfluss)
-
-
-
- Bilanzierbarer Wert (IP) -
-
- {view === 'saas' ? '0 €' : '85.000 €'} - {view === 'custom' && } -
-
Erhöht den Unternehmenswert
-
-
-
-
-
+
+
+
+
+ 💰 +

+ Software: Ausgabe oder Investment? +

+
+

+ Kumulierte Kosten im Vergleich zum bilanzierbaren + Unternehmenswert über 5 Jahre. +

- - ); +
+ +
+ {/* Control Tabs */} +
+ + +
+ + {/* Chart Area */} +
+
+ + + + + + + + + + + + + + + + + + + `€${val / 1000}k`} + axisLine={false} + tickLine={false} + tick={{ fill: "#64748b", fontSize: 12 }} + dx={-10} + /> + { + if (name === "saasCost") + return [ + formatCurrency(Number(value)), + "Kumulierte Kosten (Miete)", + ]; + if (name === "customCost") + return [ + formatCurrency(Number(value)), + "Kumulierte Kosten (Bau + Wartung)", + ]; + if (name === "customAsset") + return [ + formatCurrency(Number(value)), + "Firmenwert (Digitales Eigentum)", + ]; + if (name === "saasAsset") + return [ + formatCurrency(Number(value)), + "Firmenwert (IP)", + ]; + return [String(value), String(name)]; + }} + labelStyle={{ + fontWeight: "bold", + color: "#0f172a", + marginBottom: "4px", + fontSize: "11px", + }} + /> + + {view === "saas" ? ( + <> + + + + ) : ( + <> + + + + )} + + +
+ + {/* Summary Metrics */} +
+
+
+ Total Spend (Jahr 5) +
+
+ {view === "saas" ? "250.000 €" : "200.000 €"} + {view === "saas" && ( + + )} +
+
+ Sunk Costs (Geldabfluss) +
+
+
+
+ Bilanzierbarer Wert (IP) +
+
+ {view === "saas" ? "0 €" : "85.000 €"} + {view === "custom" && ( + + )} +
+
+ Erhöht den Unternehmenswert +
+
+
+
+
+
+
+ + ); } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index fd778dc..b5a90cb 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -2,6 +2,7 @@ "extends": "@mintel/tsconfig/nextjs.json", "compilerOptions": { "baseUrl": ".", + "strict": true, "paths": { "@/*": ["./*"], "@payload-config": ["./payload.config.ts"] diff --git a/migrate-drafts.js b/migrate-drafts.js deleted file mode 100644 index 9a0324a..0000000 --- a/migrate-drafts.js +++ /dev/null @@ -1,34 +0,0 @@ -import { getPayload } from "payload"; -import configPromise from "./apps/web/payload.config.js"; - -async function run() { - const payload = await getPayload({ config: configPromise }); - const { docs } = await payload.find({ - collection: "posts", - limit: 1000, - }); - - console.log(`Found ${docs.length} posts. Checking status...`); - - for (const doc of docs) { - if (doc._status !== "published") { - try { - await payload.update({ - collection: "posts", - id: doc.id, - data: { - _status: "published", - }, - }); - console.log(`Updated "${doc.title}" to published.`); - } catch (e) { - console.error(`Failed to update ${doc.title}:`, e.message); - } - } - } - - console.log("Migration complete."); - process.exit(0); -} - -run();