refactor(admin): split sso page.tsx into colocated components
Extract types, constants, helpers, and UI pieces (shared LoadingSkeleton/ EmptyState/StatusBadge/CopyButton, SSOConfigFormModal, DeleteConfirmModal, ConnectionTestPanel, SSOConfigCard, SSOUsersTable, SSOInfoSection) into _components/ and _types.ts to bring page.tsx from 1482 LOC to 339 LOC (under the 500 hard cap). Behavior preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
71
admin-compliance/app/sdk/sso/_components/shared.tsx
Normal file
71
admin-compliance/app/sdk/sso/_components/shared.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { CheckCircle2, Copy, XCircle } from 'lucide-react'
|
||||
|
||||
export function LoadingSkeleton({ rows = 3 }: { rows?: number }) {
|
||||
return (
|
||||
<div className="space-y-4 animate-pulse">
|
||||
{Array.from({ length: rows }).map((_, i) => (
|
||||
<div key={i} className="h-16 bg-slate-100 rounded-lg" />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function EmptyState({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
action,
|
||||
}: {
|
||||
icon: React.ReactNode
|
||||
title: string
|
||||
description: string
|
||||
action?: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
<div className="w-16 h-16 mx-auto bg-slate-100 rounded-full flex items-center justify-center mb-4">
|
||||
{icon}
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-slate-900">{title}</h3>
|
||||
<p className="mt-2 text-sm text-slate-500 max-w-md mx-auto">{description}</p>
|
||||
{action && <div className="mt-4">{action}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function StatusBadge({ enabled }: { enabled: boolean }) {
|
||||
return enabled ? (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium bg-green-100 text-green-700 rounded-full">
|
||||
<CheckCircle2 className="w-3 h-3" />
|
||||
Aktiv
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium bg-slate-100 text-slate-600 rounded-full">
|
||||
<XCircle className="w-3 h-3" />
|
||||
Deaktiviert
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function CopyButton({ value }: { value: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(value)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="p-1 text-slate-400 hover:text-slate-600 transition-colors"
|
||||
title="In Zwischenablage kopieren"
|
||||
>
|
||||
{copied ? <CheckCircle2 className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user