Compare commits
4 Commits
be071ff82f
...
e8f332da43
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8f332da43 | ||
|
|
334c6723e9 | ||
|
|
bb96b0049c | ||
|
|
0a1aa5030d |
25
src/pages/404.astro
Normal file
25
src/pages/404.astro
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
// src/pages/404.astro
|
||||||
|
import Layout from '../layouts/Layout.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<Layout title="404 - Seite nicht gefunden | N&D IT Solutions">
|
||||||
|
<main class="min-h-screen bg-[#0B0F19] text-white flex items-center justify-center p-4">
|
||||||
|
<div class="preset-card p-10 bg-slate-900/80 border border-white/10 rounded-2xl max-w-lg w-full text-center space-y-6 shadow-2xl backdrop-blur-md">
|
||||||
|
<span class="text-6xl font-extrabold text-sky-400 font-mono tracking-widest block">404</span>
|
||||||
|
<h1 class="text-2xl font-bold text-white">Seite nicht gefunden</h1>
|
||||||
|
<p class="text-slate-400 text-sm leading-relaxed">
|
||||||
|
Die von Ihnen angeforderte Seite existiert nicht oder wurde verschoben.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="pt-4">
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="inline-block bg-sky-500 hover:bg-sky-400 text-slate-950 font-bold px-6 py-3 rounded-xl text-sm transition-all shadow-lg shadow-sky-500/20 active:scale-95"
|
||||||
|
>
|
||||||
|
← Zurück zur Startseite
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</Layout>
|
||||||
@@ -19,13 +19,17 @@ const presets = Object.values(THEME_PRESETS);
|
|||||||
<h1 class="text-3xl font-extrabold tracking-tight mt-1">System & Admin Dashboard</h1>
|
<h1 class="text-3xl font-extrabold tracking-tight mt-1">System & Admin Dashboard</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
|
<a id="toggle-preview-mode" href="/?preview=draft" target="_blank" class="px-4 py-2 rounded-xl bg-amber-500/10 text-amber-400 border border-amber-500/20 text-xs font-semibold hover:bg-amber-500 hover:text-slate-950 transition-all flex items-center gap-2">
|
||||||
|
🛠️ Bearbeitungsmodus (Entwurf-Vorschau)
|
||||||
|
</a>
|
||||||
<a href="/" target="_blank" class="px-4 py-2 rounded-xl glass-panel text-xs font-semibold hover:border-sky-400 transition-all flex items-center gap-2">
|
<a href="/" target="_blank" class="px-4 py-2 rounded-xl glass-panel text-xs font-semibold hover:border-sky-400 transition-all flex items-center gap-2">
|
||||||
🌐 Live Seite Ansehen
|
🌐 Live-Seite Ansehen
|
||||||
</a>
|
</a>
|
||||||
<button id="save-all-btn" class="px-6 py-2.5 rounded-xl bg-sky-400 text-slate-950 font-bold text-xs uppercase tracking-wider hover:bg-white transition-all shadow-lg shadow-sky-400/20 active:scale-95">
|
<button id="save-all-btn" class="px-6 py-2.5 rounded-xl bg-sky-400 text-slate-950 font-bold text-xs uppercase tracking-wider hover:bg-white transition-all shadow-lg shadow-sky-400/20 active:scale-95">
|
||||||
Einstellungen Speichern
|
Einstellungen Speichern
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Quick Admin Nav Cards / Links -->
|
<!-- Quick Admin Nav Cards / Links -->
|
||||||
|
|||||||
@@ -66,15 +66,35 @@ const currentHomepageId = (siteConfig as any).site_info?.homepage_id;
|
|||||||
</td>
|
</td>
|
||||||
<td class="p-4 text-right space-x-2">
|
<td class="p-4 text-right space-x-2">
|
||||||
<a
|
<a
|
||||||
href={`/admin/editor/?page_id=${page.id}`}
|
href={page.slug}
|
||||||
class="bg-slate-800 hover:bg-slate-700 text-white px-3 py-1.5 rounded text-xs font-medium inline-block transition-colors"
|
target="_blank"
|
||||||
|
class="bg-slate-800 hover:bg-slate-700 text-sky-400 px-2.5 py-1.5 rounded text-xs font-medium inline-block transition-colors"
|
||||||
|
title="Seite im Browser öffnen"
|
||||||
>
|
>
|
||||||
✏️ Im Editor öffnen
|
🔗 Öffnen
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href={`/admin/editor/?page_id=${page.id}`}
|
||||||
|
class="bg-sky-500/20 hover:bg-sky-500 text-sky-300 hover:text-slate-950 px-2.5 py-1.5 rounded text-xs font-medium inline-block transition-colors border border-sky-500/30"
|
||||||
|
>
|
||||||
|
✏️ Editor
|
||||||
|
</a>
|
||||||
|
{!isHomepage && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-page-id={page.id}
|
||||||
|
data-page-title={page.title}
|
||||||
|
class="btn-delete-page bg-red-500/10 hover:bg-red-500 text-red-400 hover:text-white px-2.5 py-1.5 rounded text-xs font-medium inline-block transition-colors border border-red-500/20"
|
||||||
|
title="Seite löschen"
|
||||||
|
>
|
||||||
|
🗑️ Löschen
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,4 +158,28 @@ const currentHomepageId = (siteConfig as any).site_info?.homepage_id;
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Event: Seite löschen
|
||||||
|
document.querySelectorAll('.btn-delete-page').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async (e: any) => {
|
||||||
|
const pageId = e.target.getAttribute('data-page-id');
|
||||||
|
const pageTitle = e.target.getAttribute('data-page-title');
|
||||||
|
|
||||||
|
if (!confirm(`Möchten Sie die Seite "${pageTitle}" wirklich unwiderruflich löschen?`)) return;
|
||||||
|
|
||||||
|
const res = await fetch('/api/admin/pages/delete', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ page_id: pageId })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
const data = await res.json();
|
||||||
|
alert(data.error || 'Fehler beim Löschen der Seite.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
40
src/pages/api/admin/pages/delete.ts
Normal file
40
src/pages/api/admin/pages/delete.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { APIRoute } from 'astro';
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
|
const DATA_DIR = process.env.DATA_DIR || path.join(process.cwd(), 'app', 'data');
|
||||||
|
|
||||||
|
export const POST: APIRoute = async ({ request }) => {
|
||||||
|
try {
|
||||||
|
const { page_id } = await request.json();
|
||||||
|
if (!page_id) {
|
||||||
|
return new Response(JSON.stringify({ error: 'page_id erforderlich' }), { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const configPath = path.join(DATA_DIR, 'site.config.json');
|
||||||
|
let config: any = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rawData = await fs.readFile(configPath, 'utf-8');
|
||||||
|
config = JSON.parse(rawData);
|
||||||
|
} catch {
|
||||||
|
return new Response(JSON.stringify({ error: 'Konfigurationsdatei nicht gefunden' }), { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verhindere Löschen der aktiven Homepage
|
||||||
|
if (config.site_info?.homepage_id === page_id) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Die aktive Startseite kann nicht gelöscht werden.' }), { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
config.pages = (config.pages || []).filter((p: any) => p.id !== page_id);
|
||||||
|
|
||||||
|
await fs.mkdir(DATA_DIR, { recursive: true });
|
||||||
|
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ success: true, message: 'Seite erfolgreich gelöscht' }), { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Fehler beim Löschen der Seite' }), { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -71,9 +71,27 @@ export interface SiteConfig {
|
|||||||
address?: string;
|
address?: string;
|
||||||
};
|
};
|
||||||
sectionsOrder: string[];
|
sectionsOrder: string[];
|
||||||
|
|
||||||
sectionsDetails?: SectionConfig[];
|
sectionsDetails?: SectionConfig[];
|
||||||
|
site_info?: {
|
||||||
|
title?: string;
|
||||||
|
company_name?: string;
|
||||||
|
homepage_id?: string;
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
address?: string;
|
||||||
|
};
|
||||||
|
pages?: Array<{
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
is_published?: boolean;
|
||||||
|
seo?: { description?: string; og_image?: string };
|
||||||
|
sections?: Array<{ id: string; type: string; settings?: Record<string, any> }>;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface ThemeConfig {
|
export interface ThemeConfig {
|
||||||
activePreset: string;
|
activePreset: string;
|
||||||
glassBlur?: string; // 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
glassBlur?: string; // 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
||||||
@@ -175,9 +193,45 @@ export const FALLBACK_SITE_CONFIG: SiteConfig = {
|
|||||||
{ id: 'hero', type: 'hero', title: 'Hero Sektion', elements: [ { id: 'el-1', slotId: 'hero-badge-slot', type: 'badge', content: 'WaaS Engine' } ] },
|
{ id: 'hero', type: 'hero', title: 'Hero Sektion', elements: [ { id: 'el-1', slotId: 'hero-badge-slot', type: 'badge', content: 'WaaS Engine' } ] },
|
||||||
{ id: 'bento', type: 'bento', title: 'Bento Grid', elements: [] },
|
{ id: 'bento', type: 'bento', title: 'Bento Grid', elements: [] },
|
||||||
{ id: 'contact', type: 'contact', title: 'Contact Section', elements: [] }
|
{ id: 'contact', type: 'contact', title: 'Contact Section', elements: [] }
|
||||||
|
],
|
||||||
|
site_info: {
|
||||||
|
title: 'N&D IT Solutions',
|
||||||
|
homepage_id: 'page_home'
|
||||||
|
},
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
id: 'page_home',
|
||||||
|
slug: '/',
|
||||||
|
title: 'Startseite (Hauptseite)',
|
||||||
|
is_published: true,
|
||||||
|
sections: [
|
||||||
|
{ id: 'hero', type: 'HeroSection', settings: {} },
|
||||||
|
{ id: 'bento', type: 'BentoGrid', settings: {} },
|
||||||
|
{ id: 'contact', type: 'ContactSection', settings: {} }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'page_templates',
|
||||||
|
slug: '/templates',
|
||||||
|
title: 'Templates & Design-Muster',
|
||||||
|
is_published: true,
|
||||||
|
sections: [
|
||||||
|
{ id: 'hero_templates', type: 'HeroSection', settings: { title: 'Templates', subtitle: 'Unsere fertigen Design-Vorlagen' } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'page_privacy',
|
||||||
|
slug: '/datenschutz',
|
||||||
|
title: 'Datenschutzerklärung',
|
||||||
|
is_published: true,
|
||||||
|
sections: [
|
||||||
|
{ id: 'privacy_content', type: 'HeroSection', settings: { title: 'Datenschutz', subtitle: '100% DSGVO-konforme Datenverarbeitung.' } }
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const FALLBACK_THEME_CONFIG: ThemeConfig = {
|
export const FALLBACK_THEME_CONFIG: ThemeConfig = {
|
||||||
activePreset: 'corporateDark'
|
activePreset: 'corporateDark'
|
||||||
};
|
};
|
||||||
@@ -239,9 +293,12 @@ export function loadWaasConfigs(userAgent: string = '', isDraft: boolean = false
|
|||||||
...rawSite,
|
...rawSite,
|
||||||
hero: { ...FALLBACK_SITE_CONFIG.hero, ...(rawSite.hero || {}) },
|
hero: { ...FALLBACK_SITE_CONFIG.hero, ...(rawSite.hero || {}) },
|
||||||
bento: { ...FALLBACK_SITE_CONFIG.bento, ...(rawSite.bento || {}) },
|
bento: { ...FALLBACK_SITE_CONFIG.bento, ...(rawSite.bento || {}) },
|
||||||
contact: { ...FALLBACK_SITE_CONFIG.contact, ...(rawSite.contact || {}) }
|
contact: { ...FALLBACK_SITE_CONFIG.contact, ...(rawSite.contact || {}) },
|
||||||
|
pages: (rawSite as any).pages && (rawSite as any).pages.length > 0 ? (rawSite as any).pages : (FALLBACK_SITE_CONFIG as any).pages,
|
||||||
|
site_info: { ...(FALLBACK_SITE_CONFIG as any).site_info, ...((rawSite as any).site_info || {}) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const themeConfig = readJsonFile<ThemeConfig>(themePath, FALLBACK_THEME_CONFIG);
|
const themeConfig = readJsonFile<ThemeConfig>(themePath, FALLBACK_THEME_CONFIG);
|
||||||
const smtpConfig = readJsonFile<SmtpConfig>(smtpPath, FALLBACK_SMTP_CONFIG);
|
const smtpConfig = readJsonFile<SmtpConfig>(smtpPath, FALLBACK_SMTP_CONFIG);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user