feat(campaigns): audit timeline UI and manager role in role pickers

This commit is contained in:
kitos
2026-07-03 17:09:56 +02:00
parent aeaf1cf243
commit 83b4d2578a
4 changed files with 87 additions and 0 deletions
@@ -0,0 +1,67 @@
import { Clock, Loader2 } from "lucide-react";
import type { CampaignTimelineEntry } from "../api/campaigns";
interface Props {
entries: CampaignTimelineEntry[];
isLoading: boolean;
}
const ACTION_LABELS: Record<string, string> = {
create_campaign: "Campaign created",
update_campaign: "Campaign updated",
submit_campaign_for_approval: "Submitted for approval",
approve_campaign: "Approved by manager",
reject_campaign: "Rejected by manager",
activate_campaign: "Activated",
complete_campaign: "Marked completed",
request_campaign_modification: "Modification requested",
approve_campaign_modification: "Modification approved",
reject_campaign_modification: "Modification rejected",
};
export default function CampaignAuditTimeline({ entries, isLoading }: Props) {
if (isLoading) {
return (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-6 w-6 animate-spin text-gray-500" />
</div>
);
}
if (entries.length === 0) {
return (
<div className="py-8 text-center">
<Clock className="mx-auto h-8 w-8 text-gray-600" />
<p className="mt-2 text-sm text-gray-400">No timeline events yet</p>
</div>
);
}
const formatDate = (d: string) =>
new Date(d).toLocaleString("en-US", {
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
return (
<div className="relative space-y-0">
<div className="absolute left-4 top-2 bottom-2 w-px bg-gray-700" />
{entries.map((entry, idx) => (
<div key={entry.id || idx} className="relative flex gap-4 py-3">
<div className="z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-gray-700 bg-gray-800">
<Clock className="h-3.5 w-3.5 text-gray-400" />
</div>
<div className="flex-1 pt-0.5">
<p className="text-sm font-medium text-gray-200">
{ACTION_LABELS[entry.action] || entry.action}
</p>
<p className="text-xs text-gray-500">{formatDate(entry.timestamp)}</p>
</div>
</div>
))}
</div>
);
}