AI Studio Änderungen integriert #1
8 changed files with 95 additions and 4 deletions
0
bun.lock
Normal file
0
bun.lock
Normal file
0
data/database.json
Normal file
0
data/database.json
Normal file
17
server.ts
17
server.ts
|
|
@ -46,6 +46,23 @@ async function initDatabase(): Promise<AuthorData> {
|
||||||
parsed.clara = defaultAuthorData.clara;
|
parsed.clara = defaultAuthorData.clara;
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure customSection fields exist for all profiles
|
||||||
|
if (parsed.scifi && parsed.scifi.customSectionTitle === undefined) {
|
||||||
|
parsed.scifi.customSectionTitle = defaultAuthorData.scifi.customSectionTitle;
|
||||||
|
parsed.scifi.customSectionContent = defaultAuthorData.scifi.customSectionContent;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
if (parsed.erotica && parsed.erotica.customSectionTitle === undefined) {
|
||||||
|
parsed.erotica.customSectionTitle = defaultAuthorData.erotica.customSectionTitle;
|
||||||
|
parsed.erotica.customSectionContent = defaultAuthorData.erotica.customSectionContent;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
if (parsed.clara && parsed.clara.customSectionTitle === undefined) {
|
||||||
|
parsed.clara.customSectionTitle = defaultAuthorData.clara.customSectionTitle;
|
||||||
|
parsed.clara.customSectionContent = defaultAuthorData.clara.customSectionContent;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
await fs.writeFile(DATA_FILE, JSON.stringify(parsed, null, 2), "utf-8");
|
await fs.writeFile(DATA_FILE, JSON.stringify(parsed, null, 2), "utf-8");
|
||||||
|
|
|
||||||
|
|
@ -563,7 +563,7 @@ export default function AdminPanel({ onLogout }: AdminPanelProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1 md:col-span-2">
|
<div className="space-y-1 md:col-span-2">
|
||||||
<label className="text-xs font-mono text-slate-4000 uppercase text-slate-400">Über Mich / Biografie</label>
|
<label className="text-xs font-mono text-slate-400 uppercase">Über Mich / Biografie</label>
|
||||||
<textarea
|
<textarea
|
||||||
rows={8}
|
rows={8}
|
||||||
value={profile.bio}
|
value={profile.bio}
|
||||||
|
|
@ -571,6 +571,40 @@ export default function AdminPanel({ onLogout }: AdminPanelProps) {
|
||||||
className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm font-sans"
|
className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm font-sans"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Custom Highlight Section */}
|
||||||
|
<div className="md:col-span-2 border-t border-slate-800/80 pt-6 mt-2 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-bold text-white flex items-center gap-2">
|
||||||
|
<Sparkles className="w-4 h-4 text-cyan-400" />
|
||||||
|
<span>Zusätzlicher Textabschnitt (unten auf der Seite)</span>
|
||||||
|
</h4>
|
||||||
|
<p className="text-xs text-slate-500">Ein frei gestaltbarer Infobereich, der am Ende des Portfolios (nach dem Bücherregal) dargestellt wird. Ideal für Termine, Lesungen, Newsletter-Aufrufe oder persönliche Grüße. Leer lassen, um den Bereich auszublenden.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1 md:col-span-2">
|
||||||
|
<label className="text-xs font-mono text-slate-400 uppercase">Abschnitts-Titel</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={profile.customSectionTitle || ""}
|
||||||
|
onChange={(e) => updateProfileField("customSectionTitle", e.target.value)}
|
||||||
|
placeholder="z.B. Lesungen, Kontakt oder Newsletter"
|
||||||
|
className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 md:col-span-2">
|
||||||
|
<label className="text-xs font-mono text-slate-400 uppercase">Abschnitts-Inhalt (Text)</label>
|
||||||
|
<textarea
|
||||||
|
rows={4}
|
||||||
|
value={profile.customSectionContent || ""}
|
||||||
|
onChange={(e) => updateProfileField("customSectionContent", e.target.value)}
|
||||||
|
placeholder="Ihr individueller Textabschnitt..."
|
||||||
|
className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white outline-none focus:border-indigo-500 text-sm font-sans"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,22 @@ export default function ClaraPortfolio({ data }: ClaraPortfolioProps) {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Customizable Custom Section */}
|
||||||
|
{data.customSectionContent && data.customSectionContent.trim() !== "" && (
|
||||||
|
<section id="garten-notiz" className="space-y-6 pt-8 border-t border-[#4A5D50]/20">
|
||||||
|
<div className="bg-gradient-to-br from-[#4A5D50]/10 to-stone-900/40 p-8 rounded-2xl border border-[#4A5D50]/30 max-w-3xl mx-auto text-center space-y-4">
|
||||||
|
{data.customSectionTitle && (
|
||||||
|
<h3 className="text-xl md:text-2xl font-serif text-stone-100">
|
||||||
|
{data.customSectionTitle}
|
||||||
|
</h3>
|
||||||
|
)}
|
||||||
|
<p className="text-stone-300 text-sm md:text-base leading-relaxed italic whitespace-pre-wrap">
|
||||||
|
{data.customSectionContent}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Book details modular modal */}
|
{/* Book details modular modal */}
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,22 @@ export default function EroticaPortfolio({ data }: EroticaPortfolioProps) {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Customizable Custom Section */}
|
||||||
|
{data.customSectionContent && data.customSectionContent.trim() !== "" && (
|
||||||
|
<section id="exklusiv-notiz" className="space-y-6 pt-8 border-t border-orange-950/20">
|
||||||
|
<div className="bg-gradient-to-br from-orange-950/20 to-neutral-900/40 p-8 rounded-2xl border border-orange-950/40 max-w-3xl mx-auto text-center space-y-4">
|
||||||
|
{data.customSectionTitle && (
|
||||||
|
<h3 className="text-xl md:text-2xl font-serif text-amber-100">
|
||||||
|
{data.customSectionTitle}
|
||||||
|
</h3>
|
||||||
|
)}
|
||||||
|
<p className="text-neutral-300 text-sm md:text-base leading-relaxed italic whitespace-pre-wrap">
|
||||||
|
{data.customSectionContent}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Book details modular modal */}
|
{/* Book details modular modal */}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ export const defaultAuthorData: AuthorData = {
|
||||||
spotifyPlaylistId: "37i9dQZF1DX4g8Gs5YmN17",
|
spotifyPlaylistId: "37i9dQZF1DX4g8Gs5YmN17",
|
||||||
genres: ["Science-Fantasy", "Cyberpunk", "Abyssal"]
|
genres: ["Science-Fantasy", "Cyberpunk", "Abyssal"]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
customSectionTitle: "Vorträge & Messen",
|
||||||
|
customSectionContent: "Besuchen Sie mich auf der nächsten Buchmesse am Stand für spekulative Phantastik. Ich freue mich auf den intergalaktischen Austausch mit meinen Leserinnen und Lesern!"
|
||||||
},
|
},
|
||||||
erotica: {
|
erotica: {
|
||||||
name: "M. S. Velvet",
|
name: "M. S. Velvet",
|
||||||
|
|
@ -93,7 +95,9 @@ export const defaultAuthorData: AuthorData = {
|
||||||
spotifyPlaylistId: "37i9dQZF1DX2U76YeeG0Ug",
|
spotifyPlaylistId: "37i9dQZF1DX2U76YeeG0Ug",
|
||||||
genres: ["Sinnlicher Thriller", "BDSM-Touch", "Maskerade"]
|
genres: ["Sinnlicher Thriller", "BDSM-Touch", "Maskerade"]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
customSectionTitle: "Sinnliche Lesungen & Salon-Abende",
|
||||||
|
customSectionContent: "Tragen Sie sich in meinen privaten Briefverteiler ein, um exklusive Einladungen zu meinen intimen Salon-Lesungen in Berlin und Wien zu erhalten. Die Plätze sind streng limitiert."
|
||||||
},
|
},
|
||||||
clara: {
|
clara: {
|
||||||
name: "Clara Finch",
|
name: "Clara Finch",
|
||||||
|
|
@ -140,6 +144,8 @@ export const defaultAuthorData: AuthorData = {
|
||||||
spotifyPlaylistId: "37i9dQZF1DWZqd5YICuS9g",
|
spotifyPlaylistId: "37i9dQZF1DWZqd5YICuS9g",
|
||||||
genres: ["Prosa", "Natur-Essays", "Minimalismus"]
|
genres: ["Prosa", "Natur-Essays", "Minimalismus"]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
customSectionTitle: "Die Philosophie des Mooses",
|
||||||
|
customSectionContent: "Auf meinen geführten Waldspaziergängen und Schreib-Retreats lade ich Sie ein, die Stille des Waldes neu zu erfahren und Ihre Gedanken auf das Papier fließen zu lassen. Schreiben Sie mir gern für Termine und Buchungsanfragen."
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ export interface AuthorProfile {
|
||||||
heroSubtitle: string;
|
heroSubtitle: string;
|
||||||
projects: Project[];
|
projects: Project[];
|
||||||
books: Book[];
|
books: Book[];
|
||||||
|
customSectionTitle?: string;
|
||||||
|
customSectionContent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthorData {
|
export interface AuthorData {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue