import { type CSSProperties, useEffect, useState } from "react"; import { CoreApiClient } from "twenty-client-sdk/core"; import { RestApiClient } from "twenty-client-sdk/rest"; import { defineFrontComponent } from "twenty-sdk/define"; import { enqueueSnackbar, useFrontComponentExecutionContext, } from "twenty-sdk/front-component"; import { DOCUMENT_DETAILS_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from "src/constants/universal-identifiers"; type DocumentRecord = { id: string; name: string; status: string; recipientEmail: string; sajnUrl: string; }; type SyncResponse = { success: boolean; message: string; }; const styles: Record = { container: { alignItems: "center", display: "flex", fontFamily: "var(--t-font-family)", height: "100%", justifyContent: "center", padding: "24px", }, card: { background: "var(--t-background-secondary)", border: "1px solid var(--t-border-color-light)", borderRadius: "var(--t-border-radius-md)", boxSizing: "border-box", maxWidth: "560px", padding: "24px", width: "100%", }, title: { color: "var(--t-font-color-primary)", fontSize: "18px", margin: 0, }, meta: { color: "var(--t-font-color-secondary)", fontSize: "13px", margin: "8px 0 20px", }, actions: { display: "flex", gap: "8px" }, button: { border: "1px solid var(--t-border-color-medium)", borderRadius: "var(--t-border-radius-sm)", cursor: "pointer", fontFamily: "var(--t-font-family)", fontSize: "13px", padding: "8px 12px", textDecoration: "none", }, primary: { background: "var(--t-color-blue)", borderColor: "var(--t-color-blue)", color: "var(--t-font-color-inverted)", }, secondary: { background: "var(--t-background-primary)", color: "var(--t-font-color-secondary)", }, }; const DocumentDetails = () => { const recordId = useFrontComponentExecutionContext((context) => context.recordId); const [document, setDocument] = useState(null); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); const load = async () => { if (!recordId) { setLoading(false); return; } const { sajnDocuments } = await new CoreApiClient().query({ sajnDocuments: { __args: { filter: { id: { eq: recordId } }, first: 1 }, edges: { node: { id: true, name: true, status: true, recipientEmail: true, sajnUrl: true, }, }, }, }); const record = sajnDocuments?.edges?.[0]?.node; setDocument(record?.id ? (record as DocumentRecord) : null); setLoading(false); }; useEffect(() => { void load(); }, [recordId]); const sync = async () => { if (!recordId || syncing) { return; } setSyncing(true); try { const result = await new RestApiClient().post( "/s/sajn/documents/sync", { recordId }, ); await load(); await enqueueSnackbar({ message: result.message, variant: result.success ? "success" : "error", }); } catch (error) { await enqueueSnackbar({ message: error instanceof Error ? error.message : "Could not refresh the document.", variant: "error", }); } finally { setSyncing(false); } }; if (loading || !document) { return (
{loading ? "Loading…" : "Document not found."}
); } return (

{document.name}

{document.status} · {document.recipientEmail}

Open in sajn
); }; export default defineFrontComponent({ universalIdentifier: DOCUMENT_DETAILS_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, name: "sajn-document-details", description: "Shows the linked sajn document and refreshes its status.", component: DocumentDetails, });