feat: public events/polls, friends graph, captcha, SSE hardening, and admin UX
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m55s

Ship product gaps: register/vote pages, friend add/accept/decline/remove, email verify TTL, captcha on login/forgot, soft-fail user actions, SSE abort/shared client, Commando Centrum error toasts, admin delete for events/polls, and IT/NL i18n fills.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 21:08:33 +02:00
1 parent 2ff08e5127
commit ed7db6e048
76 files changed
+4834 -1376

No files matched your search

+44 -11
View File
@@ -1,11 +1,14 @@
"use client";
import { Pencil } from "lucide-react";
import { Pencil, Trash2 } from "lucide-react";
import Link from "next/link";
import { useTranslations } from "next-intl";
import { deletePoll } from "@/actions/polls";
import { useConfirmDialog } from "@/components/admin/confirm-dialog";
import { DataTable } from "@/components/admin/data-table";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useServerAction } from "@/hooks/use-server-action";
import type { DataTableColumn, PaginatedResult } from "@/types";
interface PollRow {
@@ -31,6 +34,8 @@ interface PollsTableProps {
export function PollsTable({ data }: PollsTableProps) {
const t = useTranslations("pages.admin.polls");
const { run, isPending } = useServerAction();
const { confirm, dialog: confirmDialog } = useConfirmDialog();
const statusLabel: Record<string, string> = {
active: t("statusActive"),
@@ -38,6 +43,20 @@ export function PollsTable({ data }: PollsTableProps) {
closed: t("statusClosed"),
};
async function handleDelete(row: PollRow) {
const ok = await confirm({
title: "Delete poll",
description: `Delete "${row.title}"? This cannot be undone.`,
confirmLabel: "Delete",
cancelLabel: "Cancel",
});
if (!ok) return;
run(() => deletePoll({ id: row.id }), {
successMessage: "Poll deleted!",
errorMessage: "Failed to delete poll.",
});
}
const columns: DataTableColumn<PollRow>[] = [
{ key: "id", label: t("colId"), sortable: true },
{
@@ -82,20 +101,34 @@ export function PollsTable({ data }: PollsTableProps) {
key: "actions",
label: t("colActions"),
render: (_, row) => (
<Button variant="ghost" size="icon" asChild>
<Link href={`/admin/polls/${row.id}`}>
<Pencil className="h-4 w-4" />
</Link>
</Button>
<div className="flex items-center gap-1">
<Button variant="ghost" size="icon" asChild>
<Link href={`/admin/polls/${row.id}`}>
<Pencil className="h-4 w-4" />
</Link>
</Button>
<Button
variant="ghost"
size="icon"
disabled={isPending}
onClick={() => handleDelete(row)}
aria-label="Delete poll"
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
),
},
];
return (
<DataTable
data={data}
columns={columns}
searchPlaceholder={t("searchPlaceholder")}
/>
<>
<DataTable
data={data}
columns={columns}
searchPlaceholder={t("searchPlaceholder")}
/>
{confirmDialog}
</>
);
}