"use client"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { getUserDisplayName } from "@app/lib/getUserDisplayName"; import { cn } from "@app/lib/cn"; import { formatFingerprintInfo } from "@app/lib/formatDeviceFingerprint"; import { approvalFiltersSchema, approvalQueries, type ApprovalItem } from "@app/lib/queries"; import { useQuery } from "@tanstack/react-query"; import { ArrowRight, Ban, Check, LaptopMinimal, RefreshCw } from "lucide-react"; import { useTranslations } from "next-intl"; import Link from "next/link"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { Fragment, useActionState } from "react"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { Card, CardHeader } from "./ui/card"; import { Label } from "./ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; import { Separator } from "./ui/separator"; import { InfoPopup } from "./ui/info-popup"; import { ApprovalsEmptyState } from "./ApprovalsEmptyState"; export type ApprovalFeedProps = { orgId: string; hasApprovalsEnabled: boolean; }; export function ApprovalFeed({ orgId, hasApprovalsEnabled }: ApprovalFeedProps) { const searchParams = useSearchParams(); const path = usePathname(); const t = useTranslations(); const router = useRouter(); const filters = approvalFiltersSchema.parse( Object.fromEntries(searchParams.entries()) ); const { data, isFetching, refetch } = useQuery( approvalQueries.listApprovals(orgId, filters) ); const approvals = data?.approvals ?? []; // Show empty state if no approvals are enabled for any role if (!hasApprovalsEnabled) { return ; } return (
    {approvals.map((approval, index) => (
  • refetch()} />
  • {index < approvals.length - 1 && }
    ))} {approvals.length === 0 && (
  • {t("approvalListEmpty")}
  • )}
); } type ApprovalRequestProps = { approval: ApprovalItem; orgId: string; onSuccess?: () => void; }; function ApprovalRequest({ approval, orgId, onSuccess }: ApprovalRequestProps) { const t = useTranslations(); const [_, formAction, isSubmitting] = useActionState(onSubmit, null); const api = createApiClient(useEnvContext()); async function onSubmit(_previousState: any, formData: FormData) { const decision = formData.get("decision"); const res = await api .put(`/org/${orgId}/approvals/${approval.approvalId}`, { decision }) .catch((e) => { toast({ variant: "destructive", title: t("accessApprovalErrorUpdate"), description: formatAxiosError( e, t("accessApprovalErrorUpdateDescription") ) }); }); if (res && res.status === 200) { const result = res.data.data; toast({ variant: "default", title: t("accessApprovalUpdated"), description: result.decision === "approved" ? t("accessApprovalApprovedDescription") : t("accessApprovalDeniedDescription") }); onSuccess?.(); } } return (
{getUserDisplayName({ email: approval.user.email, name: approval.user.name, username: approval.user.username })}   {approval.type === "user_device" && ( {approval.deviceName ? ( <> {t("requestingNewDeviceApproval")}:{" "} {approval.niceId ? ( {approval.deviceName} ) : ( {approval.deviceName} )} {approval.fingerprint && (
{t("deviceInformation")}
{formatFingerprintInfo(approval.fingerprint, t)}
)} ) : ( {t("requestingNewDeviceApproval")} )}
)}
{approval.decision === "pending" && (
)} {approval.decision === "approved" && ( {t("approved")} )} {approval.decision === "denied" && ( {t("denied")} )}
); }