mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 17:13:42 +00:00
feat: add admin disclosures page for reviewing data access requests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
62a56f3fc9
commit
810a13b2d8
1 changed files with 94 additions and 0 deletions
94
frontend/src/pages/DisclosuresPage.tsx
Normal file
94
frontend/src/pages/DisclosuresPage.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { Check, X } from 'lucide-react'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
|
import type { DisclosureRequest } from '@/types'
|
||||||
|
import { getDisclosureRequests, reviewDisclosure } from '@/services/disclosureService'
|
||||||
|
|
||||||
|
export function DisclosuresPage() {
|
||||||
|
const [requests, setRequests] = useState<DisclosureRequest[]>([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
const load = () => {
|
||||||
|
setLoading(true)
|
||||||
|
getDisclosureRequests('pending')
|
||||||
|
.then(setRequests)
|
||||||
|
.catch(() => setRequests([]))
|
||||||
|
.finally(() => setLoading(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => { load() }, [])
|
||||||
|
|
||||||
|
const handleReview = async (id: number, status: 'approved' | 'rejected') => {
|
||||||
|
try {
|
||||||
|
await reviewDisclosure(id, status)
|
||||||
|
load()
|
||||||
|
} catch {
|
||||||
|
// Silently reload on error
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
<h1 className="text-2xl font-bold">Freigabe-Anfragen</h1>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<Skeleton key={i} className="h-24 w-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : requests.length === 0 ? (
|
||||||
|
<p className="text-muted-foreground">Keine offenen Anfragen.</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{requests.map((dr) => (
|
||||||
|
<Card key={dr.id}>
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle className="text-base">
|
||||||
|
Fall {dr.fall_id || dr.case_id}
|
||||||
|
</CardTitle>
|
||||||
|
<Badge variant="outline">{dr.status}</Badge>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
<p className="text-sm">
|
||||||
|
<span className="text-muted-foreground">Angefragt von:</span>{' '}
|
||||||
|
{dr.requester_username || `User #${dr.requester_id}`}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">
|
||||||
|
<span className="text-muted-foreground">Begründung:</span>{' '}
|
||||||
|
{dr.reason}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{new Date(dr.created_at).toLocaleString('de-DE')}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-2 pt-1">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handleReview(dr.id, 'approved')}
|
||||||
|
>
|
||||||
|
<Check className="size-4 mr-1" />
|
||||||
|
Genehmigen
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => handleReview(dr.id, 'rejected')}
|
||||||
|
>
|
||||||
|
<X className="size-4 mr-1" />
|
||||||
|
Ablehnen
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue