Next.js admin frontend for Core with 3 categories (Communication, Infrastructure, Development), 13 modules, 2 roles (developer, ops), and 11 API proxy routes. Includes docker-compose service and nginx SSL config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
170 lines
6.1 KiB
TypeScript
170 lines
6.1 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
import Link from 'next/link'
|
||
|
||
export interface PagePurposeProps {
|
||
title: string
|
||
purpose: string
|
||
audience: string[]
|
||
gdprArticles?: string[]
|
||
architecture?: {
|
||
services: string[]
|
||
databases: string[]
|
||
diagram?: string
|
||
}
|
||
relatedPages?: Array<{
|
||
name: string
|
||
href: string
|
||
description: string
|
||
}>
|
||
collapsible?: boolean
|
||
defaultCollapsed?: boolean
|
||
}
|
||
|
||
export function PagePurpose({
|
||
title,
|
||
purpose,
|
||
audience,
|
||
gdprArticles,
|
||
architecture,
|
||
relatedPages,
|
||
collapsible = true,
|
||
defaultCollapsed = false,
|
||
}: PagePurposeProps) {
|
||
const [collapsed, setCollapsed] = useState(defaultCollapsed)
|
||
const [showArchitecture, setShowArchitecture] = useState(false)
|
||
|
||
return (
|
||
<div className="bg-gradient-to-r from-slate-50 to-slate-100 rounded-xl border border-slate-200 mb-6 overflow-hidden">
|
||
{/* Header */}
|
||
<div
|
||
className={`flex items-center justify-between px-4 py-3 ${
|
||
collapsible ? 'cursor-pointer hover:bg-slate-100' : ''
|
||
}`}
|
||
onClick={collapsible ? () => setCollapsed(!collapsed) : undefined}
|
||
>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-lg">🎯</span>
|
||
<span className="font-semibold text-slate-700">Warum gibt es diese Seite?</span>
|
||
</div>
|
||
{collapsible && (
|
||
<svg
|
||
className={`w-5 h-5 text-slate-400 transition-transform ${collapsed ? '' : 'rotate-180'}`}
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
)}
|
||
</div>
|
||
|
||
{/* Content */}
|
||
{!collapsed && (
|
||
<div className="px-4 pb-4 space-y-4">
|
||
{/* Purpose */}
|
||
<p className="text-slate-600">{purpose}</p>
|
||
|
||
{/* Metadata */}
|
||
<div className="flex flex-wrap gap-4 text-sm">
|
||
{/* Audience */}
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-slate-400">👥</span>
|
||
<span className="text-slate-500">Zielgruppe:</span>
|
||
<span className="text-slate-700">{audience.join(', ')}</span>
|
||
</div>
|
||
|
||
{/* GDPR Articles */}
|
||
{gdprArticles && gdprArticles.length > 0 && (
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-slate-400">📋</span>
|
||
<span className="text-slate-500">DSGVO-Bezug:</span>
|
||
<span className="text-slate-700">{gdprArticles.join(', ')}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Architecture (expandable) */}
|
||
{architecture && (
|
||
<div className="border-t border-slate-200 pt-3">
|
||
<button
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
setShowArchitecture(!showArchitecture)
|
||
}}
|
||
className="flex items-center gap-2 text-sm text-slate-500 hover:text-slate-700"
|
||
>
|
||
<span>🏗️</span>
|
||
<span>Architektur</span>
|
||
<svg
|
||
className={`w-4 h-4 transition-transform ${showArchitecture ? 'rotate-180' : ''}`}
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</button>
|
||
|
||
{showArchitecture && (
|
||
<div className="mt-2 p-3 bg-white rounded-lg border border-slate-200 text-sm">
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<span className="font-medium text-slate-600">Services:</span>
|
||
<ul className="mt-1 space-y-1 text-slate-500">
|
||
{architecture.services.map((service) => (
|
||
<li key={service} className="flex items-center gap-1">
|
||
<span className="w-1.5 h-1.5 bg-primary-400 rounded-full" />
|
||
{service}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<span className="font-medium text-slate-600">Datenbanken:</span>
|
||
<ul className="mt-1 space-y-1 text-slate-500">
|
||
{architecture.databases.map((db) => (
|
||
<li key={db} className="flex items-center gap-1">
|
||
<span className="w-1.5 h-1.5 bg-green-400 rounded-full" />
|
||
{db}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* Related Pages */}
|
||
{relatedPages && relatedPages.length > 0 && (
|
||
<div className="border-t border-slate-200 pt-3">
|
||
<span className="text-sm text-slate-500 flex items-center gap-2 mb-2">
|
||
<span>🔗</span>
|
||
<span>Verwandte Seiten</span>
|
||
</span>
|
||
<div className="flex flex-wrap gap-2">
|
||
{relatedPages.map((page) => (
|
||
<Link
|
||
key={page.href}
|
||
href={page.href}
|
||
className="inline-flex items-center gap-1 px-3 py-1.5 bg-white border border-slate-200 rounded-lg text-sm text-slate-600 hover:border-primary-300 hover:text-primary-600 transition-colors"
|
||
title={page.description}
|
||
>
|
||
<span>{page.name}</span>
|
||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||
</svg>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|