mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-04 19:44:47 +00:00
add way to reject a pending site
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
@@ -24,7 +25,8 @@ import {
|
||||
ArrowUpRight,
|
||||
Check,
|
||||
ChevronsUpDownIcon,
|
||||
MoreHorizontal
|
||||
MoreHorizontal,
|
||||
X
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
@@ -62,6 +64,9 @@ export default function PendingSitesTable({
|
||||
|
||||
const [isRefreshing, startTransition] = useTransition();
|
||||
const [approvingIds, setApprovingIds] = useState<Set<number>>(new Set());
|
||||
const [rejectingIds, setRejectingIds] = useState<Set<number>>(new Set());
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [selectedSite, setSelectedSite] = useState<SiteRow | null>(null);
|
||||
|
||||
const api = createApiClient(useEnvContext());
|
||||
const t = useTranslations();
|
||||
@@ -128,6 +133,33 @@ export default function PendingSitesTable({
|
||||
}
|
||||
}
|
||||
|
||||
async function rejectSite(siteId: number) {
|
||||
setRejectingIds((prev) => new Set(prev).add(siteId));
|
||||
try {
|
||||
await api.delete(`/site/${siteId}`);
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("siteDeleted"),
|
||||
variant: "default"
|
||||
});
|
||||
setIsDeleteModalOpen(false);
|
||||
setSelectedSite(null);
|
||||
router.refresh();
|
||||
} catch (e) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("siteErrorDelete"),
|
||||
description: formatAxiosError(e, t("siteErrorDelete"))
|
||||
});
|
||||
} finally {
|
||||
setRejectingIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(siteId);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const columns: ExtendedColumnDef<SiteRow>[] = [
|
||||
{
|
||||
accessorKey: "name",
|
||||
@@ -387,6 +419,7 @@ export default function PendingSitesTable({
|
||||
cell: ({ row }) => {
|
||||
const siteRow = row.original;
|
||||
const isApproving = approvingIds.has(siteRow.id);
|
||||
const isRejecting = rejectingIds.has(siteRow.id);
|
||||
return (
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
<DropdownMenu>
|
||||
@@ -409,7 +442,18 @@ export default function PendingSitesTable({
|
||||
</DropdownMenu>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={isApproving}
|
||||
disabled={isApproving || isRejecting}
|
||||
onClick={() => {
|
||||
setSelectedSite(siteRow);
|
||||
setIsDeleteModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<X className="mr-2 w-4 h-4" />
|
||||
{t("reject")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={isApproving || isRejecting}
|
||||
onClick={() => approveSite(siteRow.id)}
|
||||
>
|
||||
<Check className="mr-2 w-4 h-4" />
|
||||
@@ -446,28 +490,51 @@ export default function PendingSitesTable({
|
||||
}, 300);
|
||||
|
||||
return (
|
||||
<ControlledDataTable
|
||||
columns={columns}
|
||||
rows={sites}
|
||||
tableId="pending-sites-table"
|
||||
searchPlaceholder={t("searchSitesProgress")}
|
||||
pagination={pagination}
|
||||
onPaginationChange={handlePaginationChange}
|
||||
searchQuery={searchParams.get("query")?.toString()}
|
||||
onSearch={handleSearchChange}
|
||||
onRefresh={refreshData}
|
||||
isRefreshing={isRefreshing || isFiltering}
|
||||
refreshButtonDisabled={!canUseSiteProvisioning}
|
||||
rowCount={rowCount}
|
||||
columnVisibility={{
|
||||
niceId: false,
|
||||
nice: false,
|
||||
exitNode: false,
|
||||
address: false
|
||||
}}
|
||||
enableColumnVisibility
|
||||
stickyLeftColumn="name"
|
||||
stickyRightColumn="actions"
|
||||
/>
|
||||
<>
|
||||
{selectedSite && (
|
||||
<ConfirmDeleteDialog
|
||||
open={isDeleteModalOpen}
|
||||
setOpen={(val) => {
|
||||
setIsDeleteModalOpen(val);
|
||||
if (!val) {
|
||||
setSelectedSite(null);
|
||||
}
|
||||
}}
|
||||
dialog={
|
||||
<div className="space-y-2">
|
||||
<p>{t("siteQuestionRemove")}</p>
|
||||
<p>{t("siteMessageRemove")}</p>
|
||||
</div>
|
||||
}
|
||||
buttonText={t("siteConfirmDelete")}
|
||||
onConfirm={async () => rejectSite(selectedSite.id)}
|
||||
string={selectedSite.name}
|
||||
title={t("siteDelete")}
|
||||
/>
|
||||
)}
|
||||
<ControlledDataTable
|
||||
columns={columns}
|
||||
rows={sites}
|
||||
tableId="pending-sites-table"
|
||||
searchPlaceholder={t("searchSitesProgress")}
|
||||
pagination={pagination}
|
||||
onPaginationChange={handlePaginationChange}
|
||||
searchQuery={searchParams.get("query")?.toString()}
|
||||
onSearch={handleSearchChange}
|
||||
onRefresh={refreshData}
|
||||
isRefreshing={isRefreshing || isFiltering}
|
||||
refreshButtonDisabled={!canUseSiteProvisioning}
|
||||
rowCount={rowCount}
|
||||
columnVisibility={{
|
||||
niceId: false,
|
||||
nice: false,
|
||||
exitNode: false,
|
||||
address: false
|
||||
}}
|
||||
enableColumnVisibility
|
||||
stickyLeftColumn="name"
|
||||
stickyRightColumn="actions"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user