feat: fix the settings preview for the business licence and changes request to the approval

This commit is contained in:
Nathnael
2026-07-08 10:42:00 +00:00
parent 3bc4514b04
commit 8947ab8614
26 changed files with 1368 additions and 204 deletions

View File

@@ -1,5 +1,6 @@
import {
Alert,
Anchor,
Badge,
Box,
Button,
@@ -12,9 +13,17 @@ import {
Textarea,
} from "@mantine/core";
import { useQuery, useMutation } from "@tanstack/react-query";
import { AlertTriangle, ClipboardCheck, Clock } from "lucide-react";
import {
AlertTriangle,
ClipboardCheck,
Clock,
FilePlus2,
FileX2,
} from "lucide-react";
import { useState } from "react";
import { useFileViewer } from "@edr/ui-common";
import { fileViewUrl } from "@/constants/apiConfig";
import { api } from "@/services/api";
import type { Company, CompanyChangeRequest } from "@/types/customer";
import { formatDate, humanize } from "./format";
@@ -129,6 +138,7 @@ export function ChangeRequestReview({ company }: { company: Company }) {
api.customers.rejectChangeRequest.mutationOptions(),
);
const { view, viewer } = useFileViewer();
const [rejectId, setRejectId] = useState<string | null>(null);
const [note, setNote] = useState("");
@@ -142,6 +152,7 @@ export function ChangeRequestReview({ company }: { company: Company }) {
? Object.keys(pending.snapshot ?? {})
: ([] as string[]);
const docCount = pending?.documentFileIds?.length ?? 0;
const licenseChanges = pending?.licenseChanges ?? [];
const confirmReject = () => {
if (!rejectId) return;
@@ -206,6 +217,48 @@ export function ChangeRequestReview({ company }: { company: Company }) {
</Text>
)}
{licenseChanges.length > 0 && (
<Stack gap={8}>
<Text size="sm" fw={600} c="edr-text">
Business license changes
</Text>
{licenseChanges.map((c, i) => (
<Group key={`${c.fileId}-${i}`} gap={8} wrap="nowrap">
{c.op === "add" ? (
<FilePlus2 size={15} className="text-edr-muted" />
) : (
<FileX2 size={15} className="text-edr-muted" />
)}
<Badge
size="sm"
radius="sm"
variant="light"
color={c.op === "add" ? "green" : "red"}
>
{c.op === "add" ? "Add" : "Remove"}
</Badge>
<Anchor
component="button"
type="button"
size="sm"
onClick={() =>
view({
name: c.fileName ?? "License document",
url: fileViewUrl(c.fileId),
})
}
style={{
textDecoration:
c.op === "remove" ? "line-through" : undefined,
}}
>
{c.fileName ?? "License document"}
</Anchor>
</Group>
))}
</Stack>
)}
<Group justify="flex-end" gap="sm">
<Button
variant="light"
@@ -300,6 +353,8 @@ export function ChangeRequestReview({ company }: { company: Company }) {
</Group>
</Stack>
</Modal>
{viewer}
</>
);
}

View File

@@ -1,6 +1,7 @@
import {
ActionIcon,
Anchor,
Badge,
Box,
Button,
Card,
@@ -684,12 +685,12 @@ export default function CustomerDetailPage() {
</Text>
<Stack gap="md">
{licenseProfiles.map((p) => (
<Stack key={p.id} gap={4}>
<Stack key={p.id} gap={6}>
<Text size="sm" fw={600} c="edr-text">
{humanize(p.type)} · {p.reference}
</Text>
{(p.licenseFiles ?? []).map((f) => (
<Group key={f.url} gap={6} wrap="nowrap">
<Group key={f.id} gap={8} wrap="nowrap">
<Paperclip size={13} className="text-edr-muted" />
<Anchor
component="button"
@@ -697,16 +698,37 @@ export default function CustomerDetailPage() {
onClick={() =>
view({
name: f.name,
url: f.url,
url: fileViewUrl(f.id),
mimeType: f.mimeType,
})
}
size="xs"
style={{
textDecoration:
f.status === "pending_remove"
? "line-through"
: undefined,
}}
>
{f.name}
</Anchor>
{f.status === "pending_add" && (
<Badge size="xs" color="yellow" variant="light">
Pending approval
</Badge>
)}
{f.status === "pending_remove" && (
<Badge size="xs" color="red" variant="light">
Removal pending
</Badge>
)}
</Group>
))}
{(p.licenseFiles ?? []).length === 0 && (
<Text size="xs" c="dimmed">
No license documents.
</Text>
)}
</Stack>
))}
</Stack>

View File

@@ -37,12 +37,17 @@ export type ProfileStatus =
| "suspended"
| "blacklisted";
/** A business-license document uploaded for a company profile. */
/** Review state of a business-license file (mirrors API ProfileLicenseFileView). */
export type LicenseFileStatus = "live" | "pending_add" | "pending_remove";
/** A business-license document uploaded for a company profile (FileRecord-backed). */
export interface LicenseFile {
id: string;
name: string;
url: string;
size: number;
mimeType?: string;
mimeType: string;
/** `live` = approved; `pending_add`/`pending_remove` = awaiting review. */
status: LicenseFileStatus;
}
/** A single role a company is registered for, with its reference code. */
@@ -66,6 +71,14 @@ export interface CompanyProfile {
/** Lifecycle of a staged customer profile-edit review. */
export type ChangeRequestStatus = "pending" | "approved" | "rejected";
/** A staged business-license add/remove on one profile, awaiting review. */
export interface LicenseChangeIntent {
profileId: string;
op: "add" | "remove";
fileId: string;
fileName?: string;
}
/**
* A staged profile-edit change request. The customer's settings edits land here
* (pending) until a reviewer approves (applies them) or rejects (with a note).
@@ -77,6 +90,8 @@ export interface CompanyChangeRequest {
/** Proposed field values (the diff payload vs. the live company). */
snapshot: Record<string, unknown>;
documentFileIds: string[];
/** Staged business-license add/remove intents attached to this request. */
licenseChanges: LicenseChangeIntent[];
note: string | null;
submittedAt: string | null;
reviewedAt: string | null;