fix(admin-web): correct 12 broken API calls across 5 pages
Pages fixed:
- waitlist: GET /waitlist/list → GET /waitlist (root)
- delivery: GET /delivery/log → GET /delivery/logs; disable retry button (no backend endpoint)
- reviews: GET /reviews/list → GET /reviews; approve/reject → POST /:id/decision with body; disable flag (no endpoint)
- jobs: GET /jobs/list → GET /jobs; GET /runs/list → GET /runs; trigger → POST /jobs/trigger with {jobId}
- gdpr-export: GET /exports/list → GET /exports; POST /exports/create → POST /exports
TODO Q1: delivery retry endpoint not implemented in backend
TODO Q2: reviews flag endpoint not implemented in backend
This commit is contained in:
parent
edf8926d6d
commit
a3e94f3f3d
@ -3,10 +3,9 @@
|
|||||||
import { createProxyFetch } from '@/lib/proxy-fetch';
|
import { createProxyFetch } from '@/lib/proxy-fetch';
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { Mail, RotateCcw } from 'lucide-react';
|
import { Mail } from 'lucide-react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -63,7 +62,7 @@ export default function DeliveryPage() {
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (statusFilter !== 'all') params.set('status', statusFilter);
|
if (statusFilter !== 'all') params.set('status', statusFilter);
|
||||||
const qs = params.toString() ? `?${params.toString()}` : '';
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
||||||
const data = await apiFetch(`log${qs}`);
|
const data = await apiFetch(`logs${qs}`);
|
||||||
setEntries(Array.isArray(data?.entries) ? data.entries : Array.isArray(data) ? data : []);
|
setEntries(Array.isArray(data?.entries) ? data.entries : Array.isArray(data) ? data : []);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [statusFilter]);
|
}, [statusFilter]);
|
||||||
@ -72,10 +71,12 @@ export default function DeliveryPage() {
|
|||||||
void loadData();
|
void loadData();
|
||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
async function handleRetry(id: string) {
|
// TODO Q1: Backend has no retry endpoint for delivery log entries.
|
||||||
await apiFetch(`log/${id}/retry`, { method: 'POST' });
|
// When /delivery/logs/:id/retry is implemented, uncomment this.
|
||||||
loadData();
|
// async function handleRetry(id: string) {
|
||||||
}
|
// await apiFetch(`logs/${id}/retry`, { method: 'POST' });
|
||||||
|
// loadData();
|
||||||
|
// }
|
||||||
|
|
||||||
const deliveredCount = entries.filter(e => e.status === 'delivered').length;
|
const deliveredCount = entries.filter(e => e.status === 'delivered').length;
|
||||||
const failedCount = entries.filter(e => e.status === 'failed').length;
|
const failedCount = entries.filter(e => e.status === 'failed').length;
|
||||||
@ -190,17 +191,7 @@ export default function DeliveryPage() {
|
|||||||
{formatDate(e.createdAt)}
|
{formatDate(e.createdAt)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{e.status === 'failed' && (
|
{/* TODO Q1: Retry button disabled — backend has no retry endpoint yet */}
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-7 w-7"
|
|
||||||
onClick={() => handleRetry(e.id)}
|
|
||||||
title="Retry"
|
|
||||||
>
|
|
||||||
<RotateCcw className="h-3.5 w-3.5" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export default function GdprExportPage() {
|
|||||||
if (!userId.trim()) return;
|
if (!userId.trim()) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/exports/list?userId=${encodeURIComponent(userId)}`, {
|
const res = await fetch(`/api/exports?userId=${encodeURIComponent(userId)}`, {
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@ -55,7 +55,7 @@ export default function GdprExportPage() {
|
|||||||
setExporting(true);
|
setExporting(true);
|
||||||
setExportResult(null);
|
setExportResult(null);
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/exports/create', {
|
const res = await fetch('/api/exports', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ userId: userId.trim(), entityTypes: ['all'], format: 'json' }),
|
body: JSON.stringify({ userId: userId.trim(), entityTypes: ['all'], format: 'json' }),
|
||||||
|
|||||||
@ -70,7 +70,7 @@ export default function JobsPage() {
|
|||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [jData, rData] = await Promise.all([jobsFetch('list'), runsFetch('list?limit=50')]);
|
const [jData, rData] = await Promise.all([jobsFetch(''), runsFetch('?limit=50')]);
|
||||||
setJobs(Array.isArray(jData?.jobs) ? jData.jobs : Array.isArray(jData) ? jData : []);
|
setJobs(Array.isArray(jData?.jobs) ? jData.jobs : Array.isArray(jData) ? jData : []);
|
||||||
setRuns(Array.isArray(rData?.runs) ? rData.runs : Array.isArray(rData) ? rData : []);
|
setRuns(Array.isArray(rData?.runs) ? rData.runs : Array.isArray(rData) ? rData : []);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -81,7 +81,7 @@ export default function JobsPage() {
|
|||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
async function handleTrigger(id: string) {
|
async function handleTrigger(id: string) {
|
||||||
await jobsFetch(`${id}/trigger`, { method: 'POST' });
|
await jobsFetch('trigger', { method: 'POST', body: JSON.stringify({ jobId: id }) });
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export default function ReviewsPage() {
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (statusFilter !== 'all') params.set('status', statusFilter);
|
if (statusFilter !== 'all') params.set('status', statusFilter);
|
||||||
const qs = params.toString() ? `?${params.toString()}` : '';
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
||||||
const data = await apiFetch(`list${qs}`);
|
const data = await apiFetch(`${qs}`);
|
||||||
setReviews(Array.isArray(data?.reviews) ? data.reviews : Array.isArray(data) ? data : []);
|
setReviews(Array.isArray(data?.reviews) ? data.reviews : Array.isArray(data) ? data : []);
|
||||||
setTotal(data?.total ?? (Array.isArray(data?.reviews) ? data.reviews.length : 0));
|
setTotal(data?.total ?? (Array.isArray(data?.reviews) ? data.reviews.length : 0));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -73,19 +73,27 @@ export default function ReviewsPage() {
|
|||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
async function handleApprove(id: string) {
|
async function handleApprove(id: string) {
|
||||||
await apiFetch(`${id}/approve`, { method: 'POST' });
|
await apiFetch(`${id}/decision`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ decision: 'approved' }),
|
||||||
|
});
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleReject(id: string) {
|
async function handleReject(id: string) {
|
||||||
await apiFetch(`${id}/reject`, { method: 'POST' });
|
await apiFetch(`${id}/decision`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ decision: 'rejected' }),
|
||||||
|
});
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFlag(id: string) {
|
// TODO Q2: Backend has no /reviews/:id/flag endpoint.
|
||||||
await apiFetch(`${id}/flag`, { method: 'POST' });
|
// When flagging is implemented, uncomment this.
|
||||||
loadData();
|
// async function handleFlag(id: string) {
|
||||||
}
|
// await apiFetch(`${id}/flag`, { method: 'POST' });
|
||||||
|
// loadData();
|
||||||
|
// }
|
||||||
|
|
||||||
const pendingCount = reviews.filter(r => r.status === 'pending').length;
|
const pendingCount = reviews.filter(r => r.status === 'pending').length;
|
||||||
const flaggedCount = reviews.filter(r => r.flagged).length;
|
const flaggedCount = reviews.filter(r => r.flagged).length;
|
||||||
@ -245,12 +253,7 @@ export default function ReviewsPage() {
|
|||||||
Reject
|
Reject
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
{!r.flagged && (
|
{/* TODO Q2: Flag button disabled — no backend endpoint yet */}
|
||||||
<DropdownMenuItem onClick={() => handleFlag(r.id)}>
|
|
||||||
<Flag className="mr-2 h-4 w-4" />
|
|
||||||
Flag
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export default function WaitlistPage() {
|
|||||||
if (statusFilter !== 'all') params.set('status', statusFilter);
|
if (statusFilter !== 'all') params.set('status', statusFilter);
|
||||||
if (search) params.set('search', search);
|
if (search) params.set('search', search);
|
||||||
const qs = params.toString() ? `?${params.toString()}` : '';
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
||||||
const data = await apiFetch(`list${qs}`);
|
const data = await apiFetch(`${qs}`);
|
||||||
if (data?.entries) {
|
if (data?.entries) {
|
||||||
setEntries(data.entries);
|
setEntries(data.entries);
|
||||||
setTotal(data.total ?? data.entries.length);
|
setTotal(data.total ?? data.entries.length);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user