refactor(admin): split isms page.tsx into colocated components
Split 1260-LOC client page into _types.ts and six tab components under _components/ (Overview, Policies, SoA, Objectives, Audits, Reviews) plus a shared helpers module. Behavior preserved exactly; page.tsx is now a thin wiring shell. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
86
admin-compliance/app/sdk/isms/_components/shared.tsx
Normal file
86
admin-compliance/app/sdk/isms/_components/shared.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
// =============================================================================
|
||||
// HELPER COMPONENTS
|
||||
// =============================================================================
|
||||
|
||||
export function StatusBadge({ status, size = 'sm' }: { status: string; size?: 'sm' | 'md' }) {
|
||||
const colors: Record<string, string> = {
|
||||
ready: 'bg-green-100 text-green-700',
|
||||
compliant: 'bg-green-100 text-green-700',
|
||||
approved: 'bg-green-100 text-green-700',
|
||||
pass: 'bg-green-100 text-green-700',
|
||||
implemented: 'bg-green-100 text-green-700',
|
||||
completed: 'bg-green-100 text-green-700',
|
||||
verified: 'bg-green-100 text-green-700',
|
||||
achieved: 'bg-green-100 text-green-700',
|
||||
closed: 'bg-green-100 text-green-700',
|
||||
at_risk: 'bg-yellow-100 text-yellow-700',
|
||||
partial: 'bg-yellow-100 text-yellow-700',
|
||||
warning: 'bg-yellow-100 text-yellow-700',
|
||||
planned: 'bg-blue-100 text-blue-700',
|
||||
draft: 'bg-gray-100 text-gray-700',
|
||||
active: 'bg-blue-100 text-blue-700',
|
||||
in_progress: 'bg-blue-100 text-blue-700',
|
||||
not_ready: 'bg-red-100 text-red-700',
|
||||
non_compliant: 'bg-red-100 text-red-700',
|
||||
fail: 'bg-red-100 text-red-700',
|
||||
open: 'bg-red-100 text-red-700',
|
||||
corrective_action_pending: 'bg-orange-100 text-orange-700',
|
||||
verification_pending: 'bg-yellow-100 text-yellow-700',
|
||||
}
|
||||
const cls = colors[status] || 'bg-gray-100 text-gray-600'
|
||||
const labels: Record<string, string> = {
|
||||
ready: 'Bereit', not_ready: 'Nicht bereit', at_risk: 'Risiko',
|
||||
compliant: 'Konform', non_compliant: 'Nicht konform', partial: 'Teilweise',
|
||||
approved: 'Genehmigt', draft: 'Entwurf', pass: 'Bestanden', fail: 'Fehlgeschlagen',
|
||||
warning: 'Warnung', implemented: 'Implementiert', planned: 'Geplant',
|
||||
active: 'Aktiv', achieved: 'Erreicht', completed: 'Abgeschlossen',
|
||||
open: 'Offen', closed: 'Geschlossen', verified: 'Verifiziert',
|
||||
corrective_action_pending: 'CAPA ausstehend', verification_pending: 'Verifizierung',
|
||||
in_progress: 'In Bearbeitung',
|
||||
not_applicable: 'N/A',
|
||||
}
|
||||
const pad = size === 'md' ? 'px-3 py-1 text-sm' : 'px-2 py-0.5 text-xs'
|
||||
return <span className={`${cls} ${pad} rounded-full font-medium`}>{labels[status] || status}</span>
|
||||
}
|
||||
|
||||
export function StatCard({ label, value, sub, color = 'purple' }: { label: string; value: string | number; sub?: string; color?: string }) {
|
||||
const colors: Record<string, string> = {
|
||||
purple: 'border-purple-200 bg-purple-50',
|
||||
green: 'border-green-200 bg-green-50',
|
||||
red: 'border-red-200 bg-red-50',
|
||||
yellow: 'border-yellow-200 bg-yellow-50',
|
||||
blue: 'border-blue-200 bg-blue-50',
|
||||
}
|
||||
return (
|
||||
<div className={`border rounded-xl p-4 ${colors[color] || colors.purple}`}>
|
||||
<div className="text-2xl font-bold text-gray-900">{value}</div>
|
||||
<div className="text-sm font-medium text-gray-700 mt-1">{label}</div>
|
||||
{sub && <div className="text-xs text-gray-500 mt-0.5">{sub}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function LoadingSpinner() {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-20">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function EmptyState({ text, action, onAction }: { text: string; action?: string; onAction?: () => void }) {
|
||||
return (
|
||||
<div className="text-center py-16 text-gray-500">
|
||||
<p>{text}</p>
|
||||
{action && onAction && (
|
||||
<button onClick={onAction} className="mt-3 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 text-sm">
|
||||
{action}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user