1import { j as createSignal, g as createMemo, B as createEffect, a as createComponent, a0 as Dialog, a9 as DialogTitle, V as Typography, a1 as DialogContent, E as Box, ag as FormControl, ah as InputLabel, ai as Select, F as For, aj as MenuItem, aa as DialogActions, A as onMount, w as memo, ar as NoHydration, R as TextField, ac as InputAdornment, S as Show, as as Pagination, _ as FormControlLabel, at as Switch, au as Popover, H as Alert, ak as TableContainer, Y as Paper, al as Table, am as TableHead, an as TableRow, ao as TableCell, ap as TableBody, aq as Menu, X as Chip, Q as Stack, t as template, P as insert, ab as DialogContentText, y as delegateEvents, W as Container, N as Suspense, J as CircularProgress } from "./suid-material-beZszf02.js"; 2import { C as Close, S as Search, A as Add, e as ArrowDropUp, c as ArrowDropDown, M as MoreVert, f as Assignment, g as Description } from "./suid-icons-BVoj-b7W.js"; 3import { c as useToast, A as AppButton, d as apiClient, e as ApiClientError, l as logger, a as useNavigate, u as useAuth, j as useBilling, T as Title, r as roleChipColors, k as fetchAPIDelete, m as fetchAPIPost, n as fetchAPIPut, g as useLocation } from "./index-D967GsUD.js"; 4import { g as getCurrentUserRoleInContract, R as RoleBadge, i as isBillingPaywalled, N as Nav } from "./Nav-CablUE1w.js"; 5import { F as FirstUseModal } from "./FirstUseModal-n-Arg6Im.js"; 6import { c as checkFirstUseModalSeen, m as markFirstUseModalSeen } from "./firstUseModalHelpers-C_IqPerG.js"; 7import { c as createHelpS3Url } from "./helpS3Urls-DQj1Gxwt.js"; 8import { p as performanceLogger } from "./performanceLogger-CBSM28jc.js"; 9import { B as BindDropdowns } from "./BindDropdowns-DGjmkPlQ.js"; 10import { A as AppIconButton } from "./AppIconButton-D7UwNbyJ.js"; 11import { c as cancelContractNegotiation, r as resumeContractNegotiation, f as filenameFromContentDispositionHeader } from "./contentDispositionFilename-BDP-SJLT.js"; 12import { d as contractStatuses, f as formatContractStatusLabel, e as contractTypes } from "./browser.umd-YwivngSI.js"; 13import { C as CreateContractModal } from "./CreateContractModal-BXZCDHM6.js"; 14import { A } from "./components-B14In4Ni.js"; 15import { o as object, d as enum_, s as string, a as parse } from "./index-Ds5MqEm-.js"; 16import { F as FEATURE_CONSULTING_AGREEMENT } from "./featureFlags-DDJbx1WI.js"; 17function UpdateStatusModal(props) { 18 const toast = useToast(); 19 const [selectedStatus, setSelectedStatus] = createSignal(""); 20 const [isSubmitting, setIsSubmitting] = createSignal(false); 21 const availableStatuses = createMemo(() => ({ 22 RECEIVER_SIGNED: contractStatuses.values.RECEIVER_SIGNED, 23 INITIATOR_SIGNED: contractStatuses.values.INITIATOR_SIGNED, 24 EXECUTED: contractStatuses.values.EXECUTED 25 })); 26 createEffect(() => { 27 if (props.open && props.currentStatus) { 28 setSelectedStatus(props.currentStatus); 29 } else if (!props.open) { 30 setSelectedStatus(""); 31 setIsSubmitting(false); 32 } 33 }); 34 const formatStatusLabel = (status) => formatContractStatusLabel(status); 35 const handleContinue = async () => { 36 const status = selectedStatus(); 37 if (!status || !props.contractId) { 38 return; 39 } 40 setIsSubmitting(true); 41 try { 42 try { 43 await apiClient.contracts.update(props.contractId, { 44 status 45 }); 46 } catch (error) { 47 if (error instanceof ApiClientError && error.status === 401) { 48 window.location.href = "/"; 49 return; 50 } 51 const errorMessage = error instanceof Error ? error.message : "Failed to update contract status"; 52 toast.showToast("error", errorMessage, "Error"); 53 setIsSubmitting(false); 54 return; 55 } 56 toast.showToast("success", "Contract status updated successfully", "Success"); 57 if (props.onSuccess) { 58 props.onSuccess(); 59 } 60 props.onClose(); 61 } catch (error) { 62 logger.error({ 63 message: "[UpdateStatusModal] Failed to update contract status", 64 contractId: props.contractId, 65 error 66 }); 67 toast.showToast("error", error instanceof Error ? error.message : "Failed to update contract status. Please try again.", "Error"); 68 setIsSubmitting(false); 69 } 70 }; 71 const handleClose = () => { 72 if (!isSubmitting()) { 73 props.onClose(); 74 } 75 }; 76 const isContinueDisabled = createMemo(() => { 77 return !selectedStatus() || selectedStatus() === props.currentStatus || isSubmitting(); 78 }); 79 return createComponent(Dialog, { 80 get open() { 81 return props.open; 82 }, 83 onClose: handleClose, 84 maxWidth: "sm", 85 fullWidth: true, 86 get children() { 87 return [createComponent(DialogTitle, { 88 sx: { 89 display: "flex", 90 justifyContent: "space-between", 91 alignItems: "center" 92 }, 93 get children() { 94 return [createComponent(Typography, { 95 variant: "h6", 96 sx: { 97 fontWeight: 600 98 }, 99 children: "Update Contract Status" 100 }), createComponent(AppIconButton, { 101 onClick: handleClose, 102 get disabled() { 103 return isSubmitting(); 104 }, 105 size: "small", 106 sx: { 107 color: "inherit" 108 }, 109 get children() { 110 return createComponent(Close, {}); 111 } 112 })]; 113 } 114 }), createComponent(DialogContent, { 115 get children() { 116 return createComponent(Box, { 117 sx: { 118 paddingTop: "1rem" 119 }, 120 get children() { 121 return createComponent(FormControl, { 122 fullWidth: true, 123 get children() { 124 return [createComponent(InputLabel, { 125 id: "status-select-label", 126 children: "Status" 127 }), createComponent(Select, { 128 labelId: "status-select-label", 129 id: "status-select", 130 get value() { 131 return selectedStatus(); 132 }, 133 label: "Status", 134 onChange: (e) => setSelectedStatus(e.target.value), 135 get disabled() { 136 return isSubmitting(); 137 }, 138 MenuProps: { 139 disablePortal: true 140 }, 141 get children() { 142 return createComponent(For, { 143 get each() { 144 return Object.entries(availableStatuses()); 145 }, 146 children: ([key, value]) => createComponent(MenuItem, { 147 value, 148 "data-key": key, 149 get children() { 150 return formatStatusLabel(value); 151 } 152 }) 153 }); 154 } 155 })]; 156 } 157 }); 158 } 159 }); 160 } 161 }), createComponent(DialogActions, { 162 sx: { 163 padding: "1rem 1.5rem" 164 }, 165 get children() { 166 return [createComponent(AppButton, { 167 intent: "secondary", 168 onClick: handleClose, 169 get disabled() { 170 return isSubmitting(); 171 }, 172 children: "Cancel" 173 }), createComponent(AppButton, { 174 intent: "primary", 175 onClick: handleContinue, 176 get disabled() { 177 return isContinueDisabled(); 178 }, 179 get children() { 180 return isSubmitting() ? "Updating..." : "Continue";
181 } 182 })]; 183 } 184 })]; 185 } 186 }); 187} 188function Contracts({ 189 contracts, 190 paginationInfo, 191 onPageChange, 192 includeArchived, 193 onToggleArchived, 194 onRefreshContracts 195}) { 196 const navigate = useNavigate(); 197 const { 198 user 199 } = useAuth(); 200 const { 201 billing, 202 loading: billingLoading, 203 refetch: refetchBilling 204 } = useBilling(); 205 const toast = useToast(); 206 onMount(() => { 207 performanceLogger.logComponentMount("Contracts"); 208 performanceLogger.startComponentLoad("Contracts", "page"); 209 }); 210 createEffect(() => { 211 if (contracts() && contracts().length >= 0) { 212 performanceLogger.endComponentLoad("Contracts"); 213 } 214 }); 215 const handlePageChange = (_event, page) => { 216 onPageChange(page); 217 }; 218 const [anchorEl, setAnchorEl] = createSignal(null); 219 const [selectedContract, setSelectedContract] = createSignal(null); 220 const [searchTerm, setSearchTerm] = createSignal(""); 221 const [sortColumn, setSortColumn] = createSignal(null); 222 const [sortDirection, setSortDirection] = createSignal("asc"); 223 const [createModalOpen, setCreateModalOpen] = createSignal(false); 224 const [createModalContractId, setCreateModalContractId] = createSignal(void 0); 225 const [pendingAction, setPendingAction] = createSignal(null); 226 const [updateStatusModalOpen, setUpdateStatusModalOpen] = createSignal(false); 227 const [updateStatusContract, setUpdateStatusContract] = createSignal(null); 228 const [deleteConfirmOpen, setDeleteConfirmOpen] = createSignal(false); 229 const [deleteTargetContract, setDeleteTargetContract] = createSignal(null); 230 const [deleteLoading, setDeleteLoading] = createSignal(false); 231 const [createContractTooltipAnchor, setCreateContractTooltipAnchor] = createSignal(null); 232 const isValidTooltipAnchor = (anchor) => anchor && document.body.contains(anchor) ? anchor : null; 233 const validCreateContractTooltipAnchor = () => isValidTooltipAnchor(createContractTooltipAnchor()); 234 const [executedPdfDownloadContractId, setExecutedPdfDownloadContractId] = createSignal(null); 235 const [executedPdfStorageWarningsByContractId, setExecutedPdfStorageWarningsByContractId] = createSignal({}); 236 const formatDate = (date) => { 237 if (!date) return "-"; 238 const d = new Date(date); 239 return d.toLocaleDateString("en-US", { 240 year: "numeric", 241 month: "2-digit", 242 day: "2-digit" 243 }); 244 }; 245 const prefetchExecutedPdfStorageWarning = (contract) => { 246 const executed = contractStatusesValues().EXECUTED; 247 if (contract.status !== executed) return; 248 const cache = executedPdfStorageWarningsByContractId(); 249 if (Object.prototype.hasOwnProperty.call(cache, contract._id)) return; 250 void (async () => { 251 var _a, _b; 252 try { 253 const data = await apiClient.documents.getByContract(contract._id); 254 const raw = (_a = data.document) == null ? void 0 : _a.eSignature; 255 const esig = raw && typeof raw === "object" ? raw : null; 256 const err = ((_b = esig == null ? void 0 : esig.executedArtifactPersistLastError) == null ? void 0 : _b.trim()) ?? ""; 257 setExecutedPdfStorageWarningsByContractId((prev) => ({ 258 ...prev, 259 [contract._id]: err 260 })); 261 } catch { 262 setExecutedPdfStorageWarningsByContractId((prev) => ({ 263 ...prev, 264 [contract._id]: "" 265 })); 266 } 267 })(); 268 }; 269 const handleMenuClick = (event, contract) => { 270 setAnchorEl(event.currentTarget); 271 setSelectedContract(contract); 272 prefetchExecutedPdfStorageWarning(contract); 273 }; 274 const handleMenuClose = () => { 275 setAnchorEl(null); 276 setSelectedContract(null); 277 }; 278 const handleCreateContract = async () => { 279 if (billingLoading() && !billing()) { 280 try { 281 await refetchBilling(); 282 } catch { 283 } 284 } 285 if (isBillingPaywalled(billing())) { 286 toast.showToast("warning", "Payment is required to create a contract.", "Subscription", { 287 label: "Go to Account", 288 onClick: () => navigate("/account") 289 }); 290 return; 291 } 292 setCreateModalContractId(void 0); 293 setCreateModalOpen(true); 294 }; 295 const handleCreateModalClose = () => { 296 setCreateModalOpen(false); 297 setCreateModalContractId(void 0); 298 }; 299 const handleUpdateStatusClick = (contract) => { 300 if (document.activeElement instanceof HTMLElement) { 301 document.activeElement.blur(); 302 } 303 handleMenuClose(); 304 setUpdateStatusContract(contract); 305 requestAnimationFrame(() => { 306 setUpdateStatusModalOpen(true); 307 }); 308 }; 309 const handleUpdateStatusModalClose = () => { 310 setUpdateStatusModalOpen(false); 311 setUpdateStatusContract(null); 312 }; 313 const handleUpdateStatusSuccess = () => { 314 window.location.reload(); 315 }; 316 const isActionPending = (contractId, action) => { 317 const current = pendingAction(); 318 return (current == null ? void 0 : current.contractId) === contractId && (current == null ? void 0 : current.type) === action; 319 }; 320 const isContractCancelled = (contract) => { 321 const statuses = contractStatusesValues(); 322 return contract.status === statuses.INITIATOR_CANCELLED || contract.status === statuses.RECEIVER_CANCELLED; 323 }; 324 const canResumeNegotiation = (contract) => { 325 if (!isContractCancelled(contract)) return false;
326 const userRole = getCurrentUserRole(contract); 327 return userRole != null && contract.cancelledByRole === userRole; 328 }; 329 const getErrorMessage = (error) => { 330 if (error instanceof Error) { 331 return error.message; 332 } 333 return "Something went wrong. Please try again."; 334 }; 335 const contractTypesValues = createMemo(() => contractTypes.values); 336 const contractStatusesValues = createMemo(() => contractStatuses.values); 337 const isNegotiating = (status) => { 338 return status === contractStatusesValues().RECEIVER_REVIEW || status === contractStatusesValues().INITIATOR_REVIEW; 339 }; 340 const canUpdateStatus = (status) => { 341 const statuses = contractStatusesValues(); 342 const allowedStatuses = [statuses.SIGNATURE_REQUESTED, statuses.PENDING_SIGNATURES, statuses.RECEIVER_SIGNED, statuses.INITIATOR_SIGNED, statuses.EXECUTED]; 343 return allowedStatuses.includes(status); 344 }; 345 const getCurrentUserRole = (contract) => { 346 var _a; 347 const currentUser = user(); 348 if (!(currentUser == null ? void 0 : currentUser.id)) { 349 return null; 350 } 351 const userParty = (_a = contract.parties) == null ? void 0 : _a.find((p) => { 352 var _a2; 353 return ((_a2 = p.party) == null ? void 0 : _a2.userId) === currentUser.id; 354 }); 355 if (!userParty) { 356 return null; 357 } 358 return userParty.role === "INITIATOR" || userParty.role === "RECEIVER" ? userParty.role : null; 359 }; 360 const getCounterpartyLegalEntityName = (contract) => { 361 var _a, _b, _c; 362 const directPartyRole = getCurrentUserRole(contract); 363 const grantedByParty = contract.effectiveGrantedByParty; 364 const myRole = directPartyRole ?? (grantedByParty === "INITIATOR" || grantedByParty === "RECEIVER" ? grantedByParty : null); 365 if (!myRole) { 366 return "-"; 367 } 368 const counterpartyRole = myRole === "INITIATOR" ? "RECEIVER" : "INITIATOR"; 369 const counterparty = (_a = contract.parties) == null ? void 0 : _a.find((p) => p.role === counterpartyRole); 370 const name = (_c = (_b = counterparty == null ? void 0 : counterparty.party) == null ? void 0 : _b.legalEntityName) == null ? void 0 : _c.trim(); 371 return name && name.length > 0 ? name : "-"; 372 }; 373 const getActiveReviewer = (contract) => { 374 const status = contract.status; 375 if (status === contractStatusesValues().SIGNATURE_REQUESTED) return "INITIATOR"; 376 const reviewer = contract.documentActiveReviewer; 377 if (reviewer === "INITIATOR" || reviewer === "RECEIVER") { 378 return reviewer; 379 } 380 const documentStatus = contract.documentStatus; 381 if (documentStatus === "receiver-review") return "RECEIVER"; 382 if (documentStatus === "initiator-review" || documentStatus === "drafting") return "INITIATOR"; 383 if (status === contractStatusesValues().RECEIVER_REVIEW) return "RECEIVER"; 384 if (status === contractStatusesValues().INITIATOR_REVIEW || status === contractStatusesValues().DRAFTING) return "INITIATOR"; 385 return null; 386 }; 387 const isActivePartyOther = (contract) => { 388 const activeReviewer = getActiveReviewer(contract); 389 const currentRole = getCurrentUserRole(contract); 390 if (!activeReviewer || !currentRole) { 391 return false; 392 } 393 const status = (contract.documentStatus || contract.status || "").toString().toLowerCase(); 394 if (status === "drafting" || status === contractStatusesValues().DRAFTING.toLowerCase()) { 395 return false; 396 } 397 return activeReviewer !== currentRole; 398 }; 399 const getDocumentInstanceId = async (contractId) => { 400 var _a; 401 try { 402 const data = await apiClient.documents.getByContract(contractId); 403 if ((_a = data.document) == null ? void 0 : _a._id) { 404 return data.document._id; 405 } 406 return null; 407 } catch (error) { 408 console.error("Error fetching document instance:", error); 409 toast.showToast("error", "Failed to fetch document instance", "Error"); 410 return null; 411 } 412 }; 413 const isExecutedPdfDownloadPending = (contractId) => executedPdfDownloadContractId() === contractId; 414 const handleDownloadExecutedPdf = async (contract) => { 415 handleMenuClose(); 416 setExecutedPdfDownloadContractId(contract._id); 417 try { 418 const docInstanceId = await getDocumentInstanceId(contract._id); 419 if (!docInstanceId) { 420 toast.showToast("error", "Document instance not found for this contract", "Download Error"); 421 return; 422 } 423 const pdfResponse = await apiClient.documents.exportExecutedPdf(docInstanceId); 424 const blob = await pdfResponse.blob(); 425 const headerName = filenameFromContentDispositionHeader(pdfResponse.headers.get("Content-Disposition")); 426 const filename = headerName ?? `executed_${contract._id.slice(-8)}.pdf`; 427 const url = window.URL.createObjectURL(blob); 428 const a = document.createElement("a"); 429 a.href = url; 430 a.download = filename; 431 document.body.appendChild(a); 432 a.click(); 433 window.URL.revokeObjectURL(url); 434 document.body.removeChild(a); 435 toast.showToast("success", "Executed PDF downloaded.", "Download"); 436 } catch (error) { 437 const message = error instanceof ApiClientError ? error.message : "Failed to download executed PDF. Please try again."; 438 logger.error({ 439 message: "[Contracts] Executed PDF download failed", 440 contractId: contract._id, 441 error 442 }); 443 toast.showToast("error", message, "Download Error"); 444 } finally { 445 setExecutedPdfDownloadContractId(null); 446 } 447 }; 448 const handleDownloadPDF = async (contract) => { 449 if (contract.status === contractStatusesValues().EXECUTED) { 450 await handleDownloadExecutedPdf(contract); 451 return; 452 } 453 try { 454 handleMenuClose(); 455 const docInstanceId = await getDocumentInstanceId(contract._id); 456 if (!docInstanceId) { 457 toast.showToast("error", "Document instance not found for this contract", "Download Error"); 458 return; 459 } 460 const response = await apiClient.documents.exportPdf(docInstanceId); 461 const blob = await response.blob(); 462 const url = window.URL.createObjectURL(blob); 463 const a = document.createElement("a"); 464 a.href = url; 465 a.download = `contract_${contract._id.slice(-8)}.pdf`; 466 document.body.appendChild(a); 467 a.click(); 468 window.URL.revokeObjectURL(url); 469 document.body.removeChild(a); 470 toast.showToast("success", "PDF downloaded successfully", "Download"); 471 } catch (error) { 472 console.error("Error downloading PDF:", error); 473 toast.showToast("error", error instanceof Error ? error.message : "Failed to download PDF. Please try again.", "Download Error"); 474 } 475 }; 476 const getStatusColor = (status) => { 477 if (isNegotiating(status)) { 478 return "warning"; 479 } 480 const statuses = contractStatusesValues(); 481 switch (status) { 482 case statuses.DRAFTING: 483 return "primary"; 484 case statuses.SIGNATURE_REQUESTED: 485 case statuses.PENDING_SIGNATURES: 486 case statuses.RECEIVER_SIGNED: 487 case statuses.INITIATOR_SIGNED: 488 case statuses.EXECUTED: 489 return "success"; 490 case statuses.RECEIVER_CANCELLED: 491 case statuses.INITIATOR_CANCELLED: 492 return "error"; 493 case statuses.EXPIRED: 494 return "default"; 495 default: 496 return "default"; 497 } 498 }; 499 const formatStatus = (status) => formatContractStatusLabel(status); 500 const handleSort = (column) => { 501 if (sortColumn() === column) { 502 setSortDirection(sortDirection() === "asc" ? "desc" : "asc"); 503 } else { 504 setSortColumn(column); 505 setSortDirection("asc"); 506 } 507 }; 508 const filteredContracts = createMemo(() => { 509 const contractsData = contracts(); 510 if (!contractsData) return []; 511 if (!searchTerm()) return contractsData; 512 const term = searchTerm().toLowerCase(); 513 return contractsData.filter((contract) => { 514 var _a, _b, _c, _d; 515 const contractName = ((_a = contract.contractName) == null ? void 0 : _a.toLowerCase()) || ""; 516 const contractType = ((_b = contractTypesValues()[contract.type]) == null ? void 0 : _b.toLowerCase()) || ""; 517 const status = ((_c = contract.status) == null ? void 0 : _c.toLowerCase()) || ""; 518 const otherParty = getCounterpartyLegalEntityName(contract).toLowerCase(); 519 const organization = ((_d = contract.organizationName) == null ? void 0 : _d.toLowerCase()) || ""; 520 const issues = String(contract.issues || ""); 521 return contractName.includes(term) || contractType.includes(term) || status.includes(term) || otherParty.includes(term) || organization.includes(term) || issues.includes(term); 522 }); 523 }); 524 const sortedContracts = createMemo(() => { 525 const filtered = filteredContracts(); 526 if (!sortColumn()) return filtered; 527 const sorted = [...filtered].sort((a, b) => { 528 var _a, _b; 529 let aVal; 530 let bVal; 531 switch (sortColumn()) { 532 case "type": 533 aVal = contractTypesValues()[a.type] || ""; 534 bVal = contractTypesValues()[b.type] || ""; 535 break; 536 case "name": 537 aVal = a.contractName || ""; 538 bVal = b.contractName || ""; 539 break; 540 case "status": 541 aVal = a.status || ""; 542 bVal = b.status || ""; 543 break; 544 case "otherParty": 545 aVal = getCounterpartyLegalEntityName(a); 546 bVal = getCounterpartyLegalEntityName(b); 547 break; 548 case "organization": 549 aVal = a.organizationName || ""; 550 bVal = b.organizationName || ""; 551 break; 552 case "role": { 553 const roleA = getCurrentUserRoleInContract(a, (_a = user()) == null ? void 0 : _a.id); 554 const roleB = getCurrentUserRoleInContract(b, (_b = user()) == null ? void 0 : _b.id); 555 aVal = roleA ?? ""; 556 bVal = roleB ?? ""; 557 break; 558 } 559 case "issues": 560 aVal = a.issues || 0; 561 bVal = b.issues || 0; 562 break; 563 case "lastShared": 564 aVal = a.lastShared ? new Date(a.lastShared).getTime() : 0; 565 bVal = b.lastShared ? new Date(b.lastShared).getTime() : 0; 566 break; 567 default: 568 return 0; 569 } 570 if (sortColumn() === "issues" || sortColumn() === "lastShared") { 571 return sortDirection() === "asc" ? aVal - bVal : bVal - aVal; 572 } 573 const aStr = String(aVal).toLowerCase(); 574 const bStr = String(bVal).toLowerCase(); 575 if (aStr < bStr) return sortDirection() === "asc" ? -1 : 1; 576 if (aStr > bStr) return sortDirection() === "asc" ? 1 : -1; 577 return 0; 578 }); 579 return sorted; 580 });
581 createEffect(() => { 582 for (const contract of sortedContracts()) { 583 prefetchExecutedPdfStorageWarning(contract); 584 } 585 }); 586 const contractsWithExecutedPdfStorageWarnings = createMemo(() => { 587 const warnings = executedPdfStorageWarningsByContractId(); 588 return sortedContracts().map((c) => { 589 const msg = warnings[c._id]; 590 if (!msg) return null; 591 return { 592 contractId: c._id, 593 contractName: typeof c.contractName === "string" && c.contractName.trim().length > 0 ? c.contractName : "Contract", 594 message: msg 595 }; 596 }).filter((row) => row != null); 597 }); 598 const handleEndNegotiationsAction = async (contract) => { 599 handleMenuClose(); 600 if (isActivePartyOther(contract)) { 601 toast.showToast("error", "Only the active party can cancel negotiations right now."); 602 return; 603 } 604 setPendingAction({ 605 contractId: contract._id, 606 type: "cancel" 607 }); 608 try { 609 await cancelContractNegotiation(contract._id); 610 toast.showToast("success", "Negotiations ended. You can resume at any time."); 611 } catch (error) { 612 logger.error({ 613 message: "[Contracts] Failed to cancel negotiations from dashboard list", 614 contractId: contract._id, 615 error 616 }); 617 toast.showToast("error", getErrorMessage(error)); 618 } finally { 619 setPendingAction(null); 620 } 621 }; 622 const handleResumeNegotiationsAction = async (contract) => { 623 handleMenuClose(); 624 if (!canResumeNegotiation(contract)) { 625 toast.showToast("error", "Only the party who ended negotiations can resume them."); 626 return; 627 } 628 setPendingAction({ 629 contractId: contract._id, 630 type: "resume" 631 }); 632 try { 633 await resumeContractNegotiation(contract._id); 634 toast.showToast("success", "Negotiations resumed."); 635 window.location.reload(); 636 } catch (error) { 637 logger.error({ 638 message: "[Contracts] Failed to resume negotiations from dashboard list", 639 contractId: contract._id, 640 error 641 }); 642 toast.showToast("error", getErrorMessage(error)); 643 } finally { 644 setPendingAction(null); 645 } 646 }; 647 const handleArchiveContract = async (contract) => { 648 handleMenuClose(); 649 try { 650 const newArchived = !contract.isArchived; 651 await apiClient.contracts.archive(contract._id, newArchived); 652 toast.showToast("success", newArchived ? "Contract archived" : "Contract unarchived", "Contract"); 653 if (onRefreshContracts) onRefreshContracts(); 654 } catch (error) { 655 logger.error({ 656 message: "[Contracts] Failed to archive contract", 657 contractId: contract._id, 658 error 659 }); 660 toast.showToast("error", getErrorMessage(error)); 661 } 662 }; 663 const handleDeleteClick = (contract) => { 664 if (document.activeElement instanceof HTMLElement) { 665 document.activeElement.blur(); 666 } 667 handleMenuClose(); 668 setDeleteTargetContract(contract); 669 requestAnimationFrame(() => { 670 setDeleteConfirmOpen(true); 671 }); 672 }; 673 const handleDeleteConfirm = async () => { 674 const contract = deleteTargetContract(); 675 if (!contract) return; 676 setDeleteLoading(true); 677 try { 678 await apiClient.contracts.delete(contract._id); 679 toast.showToast("success", "Contract deleted", "Contract"); 680 setDeleteConfirmOpen(false); 681 setDeleteTargetContract(null); 682 if (onRefreshContracts) onRefreshContracts(); 683 } catch (error) { 684 logger.error({ 685 message: "[Contracts] Failed to delete contract", 686 contractId: contract._id, 687 error 688 }); 689 toast.showToast("error", getErrorMessage(error)); 690 } finally { 691 setDeleteLoading(false); 692 } 693 }; 694 const handleDeleteCancel = () => { 695 setDeleteConfirmOpen(false); 696 setDeleteTargetContract(null); 697 }; 698 const isContractOwner = (contract) => { 699 return getCurrentUserRole(contract) === "INITIATOR"; 700 }; 701 const isContractAdmin = (contract) => { 702 const role = getCurrentUserRole(contract); 703 return role === "INITIATOR" || role === "RECEIVER"; 704 }; 705 return [createComponent(CreateContractModal, { 706 get open() { 707 return createModalOpen(); 708 }, 709 get contractId() { 710 return createModalContractId(); 711 }, 712 onClose: handleCreateModalClose 713 }), createComponent(UpdateStatusModal, { 714 get open() { 715 return updateStatusModalOpen(); 716 }, 717 get contractId() { 718 var _a; 719 return ((_a = updateStatusContract()) == null ? void 0 : _a._id) || null; 720 }, 721 get currentStatus() { 722 var _a; 723 return ((_a = updateStatusContract()) == null ? void 0 : _a.status) || null; 724 }, 725 onClose: handleUpdateStatusModalClose, 726 onSuccess: handleUpdateStatusSuccess 727 }), createComponent(Dialog, { 728 get open() { 729 return deleteConfirmOpen(); 730 },
731 onClose: handleDeleteCancel, 732 get children() { 733 return [createComponent(DialogTitle, { 734 children: "Delete Contract" 735 }), createComponent(DialogContent, { 736 get children() { 737 return createComponent(Typography, { 738 get children() { 739 return ['Are you sure you want to delete "', memo(() => { 740 var _a; 741 return ((_a = deleteTargetContract()) == null ? void 0 : _a.contractName) || "this contract"; 742 }), '"? This action cannot be undone.']; 743 } 744 }); 745 } 746 }), createComponent(DialogActions, { 747 get children() { 748 return [createComponent(AppButton, { 749 intent: "secondary", 750 onClick: handleDeleteCancel, 751 get disabled() { 752 return deleteLoading(); 753 }, 754 children: "Cancel" 755 }), createComponent(AppButton, { 756 intent: "destructive", 757 onClick: () => void handleDeleteConfirm(), 758 get disabled() { 759 return deleteLoading(); 760 }, 761 "data-testid": "confirm-delete-button", 762 get children() { 763 return deleteLoading() ? "Deleting..." : "Delete"; 764 } 765 })]; 766 } 767 })]; 768 } 769 }), createComponent(Title, { 770 children: "Contracts" 771 }), createComponent(Box, { 772 sx: { 773 width: "100%", 774 height: "100%", 775 display: "flex", 776 flexDirection: "column" 777 }, 778 get children() { 779 return [createComponent(Box, { 780 sx: { 781 display: "flex", 782 justifyContent: "space-between", 783 alignItems: "center", 784 marginBottom: "1rem", 785 gap: "1rem", 786 flexShrink: 0 787 }, 788 get children() { 789 return [createComponent(NoHydration, { 790 get children() { 791 return createComponent(TextField, { 792 placeholder: "Search contracts...", 793 get value() { 794 return searchTerm(); 795 }, 796 onChange: (e) => setSearchTerm(e.target.value), 797 sx: { 798 flexGrow: 1, 799 maxWidth: "400px", 800 "& .MuiInputBase-input::placeholder": { 801 color: "primary.light", 802 opacity: 1 803 }, 804 "& .MuiInputBase-input": { 805 color: "black" 806 } 807 }, 808 size: "small", 809 get InputProps() { 810 return { 811 startAdornment: createComponent(InputAdornment, { 812 position: "start", 813 get children() { 814 return createComponent(Search, {}); 815 } 816 }) 817 }; 818 } 819 }); 820 } 821 }), createComponent(Show, { 822 get when() { 823 return memo(() => !!paginationInfo())() && paginationInfo().totalPages > 1; 824 }, 825 get children() { 826 return createComponent(Box, { 827 sx: { 828 display: "flex", 829 justifyContent: "center", 830 padding: "1rem", 831 flexShrink: 0 832 }, 833 get children() { 834 return createComponent(Pagination, { 835 get count() { 836 return paginationInfo().totalPages; 837 }, 838 get page() { 839 return paginationInfo().currentPage; 840 }, 841 onChange: handlePageChange, 842 color: "primary", 843 showFirstButton: true, 844 showLastButton: true 845 }); 846 } 847 }); 848 } 849 }), createComponent(Show, { 850 when: onToggleArchived, 851 get children() { 852 return createComponent(FormControlLabel, { 853 get control() { 854 return createComponent(Switch, { 855 get checked() { 856 return (includeArchived == null ? void 0 : includeArchived()) ?? false; 857 }, 858 onChange: (_, checked) => onToggleArchived == null ? void 0 : onToggleArchived(checked), 859 size: "small", 860 "data-testid": "show-archived-toggle" 861 }); 862 }, 863 get label() { 864 return createComponent(Typography, { 865 variant: "body2", 866 children: "Show Archived" 867 }); 868 }, 869 sx: { 870 marginRight: "0.5rem" 871 } 872 }); 873 } 874 }), createComponent(Box, { 875 component: "span", 876 onMouseEnter: (e) => setCreateContractTooltipAnchor(e.currentTarget), 877 onMouseLeave: () => setCreateContractTooltipAnchor(null), 878 sx: { 879 display: "inline-block" 880 }, 881 get children() { 882 return [createComponent(AppButton, { 883 intent: "primary", 884 get startIcon() { 885 return createComponent(Add, {}); 886 }, 887 onClick: () => void handleCreateContract(), 888 children: "Create New Contract" 889 }), createComponent(Popover, { 890 get open() { 891 return !!validCreateContractTooltipAnchor(); 892 }, 893 get anchorEl() { 894 return validCreateContractTooltipAnchor() ?? void 0; 895 }, 896 onClose: () => setCreateContractTooltipAnchor(null), 897 anchorOrigin: { 898 vertical: "top", 899 horizontal: "center" 900 }, 901 transformOrigin: { 902 vertical: "bottom", 903 horizontal: "center" 904 }, 905 disableRestoreFocus: true, 906 sx: { 907 pointerEvents: "none" 908 }, 909 get children() { 910 return createComponent(Typography, { 911 variant: "body2", 912 sx: { 913 padding: "0.75rem", 914 maxWidth: "300px" 915 }, 916 children: "This will create a new contract from scratch to send to a counterparty" 917 }); 918 } 919 })]; 920 } 921 })]; 922 } 923 }), createComponent(Show, { 924 get when() { 925 return contractsWithExecutedPdfStorageWarnings().length > 0; 926 }, 927 get children() { 928 return createComponent(Box, { 929 sx: { 930 marginBottom: "1rem", 931 flexShrink: 0 932 }, 933 get children() { 934 return createComponent(For, { 935 get each() { 936 return contractsWithExecutedPdfStorageWarnings(); 937 }, 938 children: (row) => createComponent(Alert, { 939 severity: "warning", 940 sx: { 941 marginBottom: "0.75rem" 942 }, 943 get children() { 944 return [createComponent(Typography, { 945 component: "div", 946 variant: "body2", 947 sx: { 948 fontWeight: 600, 949 marginBottom: "0.25rem" 950 }, 951 get children() { 952 return ["Could not save the executed PDF to Transactency storage (", memo(() => row.contractName), ")"]; 953 } 954 }), createComponent(Typography, { 955 component: "div", 956 variant: "body2", 957 sx: { 958 wordBreak: "break-word" 959 }, 960 children: "You can still download the file from the eSignature provider. If this keeps happening, share the detail below with support." 961 }), createComponent(Typography, { 962 component: "div", 963 variant: "caption", 964 sx: { 965 display: "block", 966 marginTop: "0.5rem", 967 fontFamily: "ui-monospace, monospace", 968 wordBreak: "break-word" 969 }, 970 get children() { 971 return row.message; 972 } 973 })]; 974 } 975 }) 976 }); 977 } 978 }); 979 } 980 }), createComponent(TableContainer, { 981 component: Paper, 982 sx: { 983 flexGrow: 1, 984 overflow: "auto" 985 }, 986 get children() { 987 return createComponent(Table, { 988 get children() { 989 return [createComponent(TableHead, { 990 get children() { 991 return createComponent(TableRow, { 992 get children() { 993 return [createComponent(TableCell, { 994 sx: { 995 width: "1.8rem" 996 } 997 }), createComponent(TableCell, { 998 sx: { 999 cursor: "pointer", 1000 userSelect: "none" 1001 }, 1002 onClick: () => handleSort("type"), 1003 get children() { 1004 return createComponent(Box, { 1005 sx: { 1006 display: "flex", 1007 alignItems: "center", 1008 gap: "0.5rem" 1009 }, 1010 get children() { 1011 return [createComponent(Typography, { 1012 variant: "body2", 1013 color: "text.secondary", 1014 children: "Type" 1015 }), createComponent(Box, { 1016 get sx() { 1017 return { 1018 display: "flex", 1019 flexDirection: "column", 1020 opacity: sortColumn() === "type" ? 1 : 0.3 1021 }; 1022 }, 1023 get children() { 1024 return [createComponent(ArrowDropUp, { 1025 get sx() { 1026 return { 1027 fontSize: "0.875rem", 1028 opacity: sortColumn() === "type" && sortDirection() === "asc" ? 1 : 0.3, 1029 transition: "opacity 0.2s" 1030 }; 1031 } 1032 }), createComponent(ArrowDropDown, { 1033 get sx() { 1034 return { 1035 fontSize: "0.875rem", 1036 marginTop: "-0.25rem", 1037 opacity: sortColumn() === "type" && sortDirection() === "desc" ? 1 : 0.3, 1038 transition: "opacity 0.2s" 1039 }; 1040 } 1041 })]; 1042 } 1043 })]; 1044 } 1045 }); 1046 } 1047 }), createComponent(TableCell, { 1048 sx: { 1049 cursor: "pointer", 1050 userSelect: "none" 1051 }, 1052 onClick: () => handleSort("name"), 1053 get children() { 1054 return createComponent(Box, { 1055 sx: { 1056 display: "flex", 1057 alignItems: "center", 1058 gap: "0.5rem" 1059 }, 1060 get children() { 1061 return [createComponent(Typography, { 1062 variant: "body2", 1063 color: "text.secondary", 1064 children: "Name" 1065 }), createComponent(Box, { 1066 get sx() { 1067 return { 1068 display: "flex", 1069 flexDirection: "column",
1070 opacity: sortColumn() === "name" ? 1 : 0.3 1071 }; 1072 }, 1073 get children() { 1074 return [createComponent(ArrowDropUp, { 1075 get sx() { 1076 return { 1077 fontSize: "0.875rem", 1078 opacity: sortColumn() === "name" && sortDirection() === "asc" ? 1 : 0.3, 1079 transition: "opacity 0.2s" 1080 }; 1081 } 1082 }), createComponent(ArrowDropDown, { 1083 get sx() { 1084 return { 1085 fontSize: "0.875rem", 1086 marginTop: "-0.25rem", 1087 opacity: sortColumn() === "name" && sortDirection() === "desc" ? 1 : 0.3, 1088 transition: "opacity 0.2s" 1089 }; 1090 } 1091 })]; 1092 } 1093 })]; 1094 } 1095 }); 1096 } 1097 }), createComponent(TableCell, { 1098 sx: { 1099 cursor: "pointer", 1100 userSelect: "none" 1101 }, 1102 onClick: () => handleSort("role"), 1103 get children() { 1104 return createComponent(Box, { 1105 sx: { 1106 display: "flex", 1107 alignItems: "center", 1108 gap: "0.5rem" 1109 }, 1110 get children() { 1111 return [createComponent(Typography, { 1112 variant: "body2", 1113 color: "text.secondary", 1114 children: "My Role" 1115 }), createComponent(Box, { 1116 get sx() { 1117 return { 1118 display: "flex", 1119 flexDirection: "column", 1120 opacity: sortColumn() === "role" ? 1 : 0.3 1121 }; 1122 }, 1123 get children() { 1124 return [createComponent(ArrowDropUp, { 1125 get sx() { 1126 return { 1127 fontSize: "0.875rem", 1128 opacity: sortColumn() === "role" && sortDirection() === "asc" ? 1 : 0.3, 1129 transition: "opacity 0.2s" 1130 }; 1131 } 1132 }), createComponent(ArrowDropDown, { 1133 get sx() { 1134 return { 1135 fontSize: "0.875rem", 1136 marginTop: "-0.25rem", 1137 opacity: sortColumn() === "role" && sortDirection() === "desc" ? 1 : 0.3, 1138 transition: "opacity 0.2s" 1139 }; 1140 } 1141 })]; 1142 } 1143 })]; 1144 } 1145 }); 1146 } 1147 }), createComponent(TableCell, { 1148 sx: { 1149 cursor: "pointer", 1150 userSelect: "none" 1151 }, 1152 onClick: () => handleSort("status"), 1153 get children() { 1154 return createComponent(Box, { 1155 sx: { 1156 display: "flex", 1157 alignItems: "center", 1158 gap: "0.5rem" 1159 }, 1160 get children() { 1161 return [createComponent(Typography, { 1162 variant: "body2", 1163 color: "text.secondary", 1164 children: "Status" 1165 }), createComponent(Box, { 1166 get sx() { 1167 return { 1168 display: "flex", 1169 flexDirection: "column", 1170 opacity: sortColumn() === "status" ? 1 : 0.3 1171 }; 1172 }, 1173 get children() { 1174 return [createComponent(ArrowDropUp, { 1175 get sx() { 1176 return { 1177 fontSize: "0.875rem", 1178 opacity: sortColumn() === "status" && sortDirection() === "asc" ? 1 : 0.3, 1179 transition: "opacity 0.2s" 1180 }; 1181 } 1182 }), createComponent(ArrowDropDown, { 1183 get sx() { 1184 return { 1185 fontSize: "0.875rem", 1186 marginTop: "-0.25rem", 1187 opacity: sortColumn() === "status" && sortDirection() === "desc" ? 1 : 0.3, 1188 transition: "opacity 0.2s" 1189 }; 1190 } 1191 })]; 1192 } 1193 })]; 1194 } 1195 }); 1196 } 1197 }), createComponent(TableCell, { 1198 sx: { 1199 cursor: "pointer", 1200 userSelect: "none" 1201 }, 1202 onClick: () => handleSort("otherParty"), 1203 get children() { 1204 return createComponent(Box, { 1205 sx: { 1206 display: "flex", 1207 alignItems: "center", 1208 gap: "0.5rem" 1209 }, 1210 get children() { 1211 return [createComponent(Typography, { 1212 variant: "body2", 1213 color: "text.secondary", 1214 children: "Other Party" 1215 }), createComponent(Box, { 1216 get sx() { 1217 return { 1218 display: "flex", 1219 flexDirection: "column", 1220 opacity: sortColumn() === "otherParty" ? 1 : 0.3 1221 }; 1222 }, 1223 get children() { 1224 return [createComponent(ArrowDropUp, { 1225 get sx() { 1226 return { 1227 fontSize: "0.875rem", 1228 opacity: sortColumn() === "otherParty" && sortDirection() === "asc" ? 1 : 0.3, 1229 transition: "opacity 0.2s" 1230 }; 1231 } 1232 }), createComponent(ArrowDropDown, { 1233 get sx() { 1234 return { 1235 fontSize: "0.875rem", 1236 marginTop: "-0.25rem", 1237 opacity: sortColumn() === "otherParty" && sortDirection() === "desc" ? 1 : 0.3, 1238 transition: "opacity 0.2s" 1239 }; 1240 } 1241 })]; 1242 } 1243 })]; 1244 } 1245 }); 1246 } 1247 }), createComponent(TableCell, { 1248 sx: { 1249 cursor: "pointer", 1250 userSelect: "none" 1251 }, 1252 onClick: () => handleSort("issues"), 1253 get children() { 1254 return createComponent(Box, { 1255 sx: { 1256 display: "flex", 1257 alignItems: "center", 1258 gap: "0.5rem" 1259 }, 1260 get children() { 1261 return [createComponent(Typography, { 1262 variant: "body2", 1263 color: "text.secondary", 1264 children: "Issues" 1265 }), createComponent(Box, { 1266 get sx() { 1267 return { 1268 display: "flex", 1269 flexDirection: "column", 1270 opacity: sortColumn() === "issues" ? 1 : 0.3 1271 }; 1272 }, 1273 get children() { 1274 return [createComponent(ArrowDropUp, { 1275 get sx() { 1276 return { 1277 fontSize: "0.875rem", 1278 opacity: sortColumn() === "issues" && sortDirection() === "asc" ? 1 : 0.3, 1279 transition: "opacity 0.2s" 1280 }; 1281 } 1282 }), createComponent(ArrowDropDown, { 1283 get sx() { 1284 return { 1285 fontSize: "0.875rem", 1286 marginTop: "-0.25rem", 1287 opacity: sortColumn() === "issues" && sortDirection() === "desc" ? 1 : 0.3, 1288 transition: "opacity 0.2s" 1289 }; 1290 } 1291 })]; 1292 } 1293 })]; 1294 } 1295 }); 1296 } 1297 }), createComponent(TableCell, { 1298 sx: { 1299 cursor: "pointer", 1300 userSelect: "none" 1301 }, 1302 onClick: () => handleSort("lastShared"), 1303 get children() { 1304 return createComponent(Box, { 1305 sx: { 1306 display: "flex", 1307 alignItems: "center", 1308 gap: "0.5rem" 1309 }, 1310 get children() { 1311 return [createComponent(Typography, { 1312 variant: "body2", 1313 color: "text.secondary", 1314 children: "Last Shared" 1315 }), createComponent(Box, { 1316 get sx() { 1317 return { 1318 display: "flex", 1319 flexDirection: "column", 1320 opacity: sortColumn() === "lastShared" ? 1 : 0.3 1321 }; 1322 }, 1323 get children() { 1324 return [createComponent(ArrowDropUp, { 1325 get sx() { 1326 return { 1327 fontSize: "0.875rem", 1328 opacity: sortColumn() === "lastShared" && sortDirection() === "asc" ? 1 : 0.3, 1329 transition: "opacity 0.2s" 1330 }; 1331 } 1332 }), createComponent(ArrowDropDown, { 1333 get sx() { 1334 return { 1335 fontSize: "0.875rem", 1336 marginTop: "-0.25rem", 1337 opacity: sortColumn() === "lastShared" && sortDirection() === "desc" ? 1 : 0.3, 1338 transition: "opacity 0.2s" 1339 }; 1340 } 1341 })]; 1342 } 1343 })]; 1344 } 1345 }); 1346 } 1347 })]; 1348 } 1349 }); 1350 } 1351 }), createComponent(TableBody, { 1352 get children() { 1353 return createComponent(For, { 1354 get each() { 1355 return sortedContracts(); 1356 }, 1357 children: (contract) => createComponent(TableRow, { 1358 get children() { 1359 return [createComponent(TableCell, { 1360 get children() { 1361 return [createComponent(AppIconButton, { 1362 onClick: (e) => handleMenuClick(e, contract), 1363 get children() { 1364 return createComponent(MoreVert, { 1365 color: "primary" 1366 }); 1367 } 1368 }), createComponent(Menu, { 1369 get anchorEl() { 1370 return anchorEl(); 1371 }, 1372 get open() { 1373 var _a; 1374 return memo(() => !!Boolean(anchorEl()))() && ((_a = selectedContract()) == null ? void 0 : _a._id) === contract._id; 1375 }, 1376 onClose: handleMenuClose, 1377 anchorOrigin: { 1378 vertical: "bottom", 1379 horizontal: "left" 1380 }, 1381 transformOrigin: { 1382 vertical: "top", 1383 horizontal: "left" 1384 }, 1385 get children() { 1386 return [createComponent(MenuItem, { 1387 onClick: () => navigate(`/playbook/${contract._id}`), 1388 sx: { 1389 color: "primary.main" 1390 }, 1391 children: "Open" 1392 }), createComponent(MenuItem, { 1393
1393 onClick: () => void handleDownloadPDF(contract), 1394 get disabled() { 1395 return isExecutedPdfDownloadPending(contract._id); 1396 }, 1397 sx: { 1398 color: "primary.main" 1399 }, 1400 "data-testid": "download-pdf-button", 1401 get children() { 1402 return isExecutedPdfDownloadPending(contract._id) ? "Downloading PDFâ¦" : "Download PDF"; 1403 } 1404 }), createComponent(MenuItem, { 1405 onClick: () => handleUpdateStatusClick(contract), 1406 get disabled() { 1407 return !canUpdateStatus(contract.status); 1408 }, 1409 sx: { 1410 color: "primary.main" 1411 }, 1412 children: "Update Status" 1413 }), createComponent(MenuItem, { 1414 onClick: () => void handleArchiveContract(contract), 1415 get disabled() { 1416 return !isContractAdmin(contract); 1417 }, 1418 sx: { 1419 color: "primary.main" 1420 }, 1421 "data-testid": "archive-contract-button", 1422 get children() { 1423 return contract.isArchived ? "Unarchive" : "Archive"; 1424 } 1425 }), createComponent(MenuItem, { 1426 onClick: () => handleDeleteClick(contract), 1427 get disabled() { 1428 return !isContractOwner(contract); 1429 }, 1430 sx: { 1431 color: "error.main" 1432 }, 1433 "data-testid": "delete-contract-button", 1434 children: "Delete" 1435 }), createComponent(MenuItem, { 1436 onClick: () => void handleEndNegotiationsAction(contract), 1437 get disabled() { 1438 return isActionPending(contract._id, "cancel") || isActivePartyOther(contract) || contract.status === contractStatuses.values.DRAFTING; 1439 }, 1440 sx: { 1441 color: "primary.main" 1442 }, 1443 get children() { 1444 return isActionPending(contract._id, "cancel") ? "Cancelling..." : "Cancel Negotiations"; 1445 } 1446 }), createComponent(MenuItem, { 1447 onClick: () => void handleResumeNegotiationsAction(contract), 1448 get disabled() { 1449 return !canResumeNegotiation(contract) || isActionPending(contract._id, "resume"); 1450 }, 1451 sx: { 1452 color: "primary.main" 1453 }, 1454 get children() { 1455 return isActionPending(contract._id, "resume") ? "Resuming..." : "Resume Negotiations"; 1456 } 1457 })]; 1458 } 1459 })]; 1460 } 1461 }), createComponent(TableCell, { 1462 sx: { 1463 fontWeight: 600 1464 }, 1465 get children() { 1466 return contractTypesValues()[contract.type]; 1467 } 1468 }), createComponent(TableCell, { 1469 get children() { 1470 return createComponent(A, { 1471 get href() { 1472 return `/playbook/${contract._id}`; 1473 }, 1474 "class": "light", 1475 get children() { 1476 return contract.contractName; 1477 } 1478 }); 1479 } 1480 }), createComponent(TableCell, { 1481 get children() { 1482 return (() => { 1483 var _a; 1484 const partyRole = getCurrentUserRoleInContract(contract, (_a = user()) == null ? void 0 : _a.id); 1485 const docRole = contract.effectiveDocumentRole; 1486 const grantedByParty = contract.effectiveGrantedByParty; 1487 if (docRole && grantedByParty) { 1488 return createComponent(RoleBadge, { 1489 role: grantedByParty, 1490 documentRole: docRole, 1491 showPartyLabel: true 1492 }); 1493 } else if (partyRole) { 1494 return createComponent(RoleBadge, { 1495 role: partyRole 1496 }); 1497 } 1498 return "-"; 1499 })(); 1500 } 1501 }), createComponent(TableCell, { 1502 get children() { 1503 return createComponent(Chip, { 1504 get label() { 1505 return formatStatus(contract.status); 1506 }, 1507 get color() { 1508 return getStatusColor(contract.status); 1509 }, 1510 size: "small", 1511 get sx() { 1512 return { 1513 textTransform: "capitalize", 1514 fontWeight: 600, 1515 ...contract.status === contractStatusesValues().RECEIVER_REVIEW && { 1516 backgroundColor: roleChipColors.receiver, 1517 color: roleChipColors.text 1518 }, 1519 ...contract.status === contractStatusesValues().INITIATOR_REVIEW && { 1520 backgroundColor: roleChipColors.initiator, 1521 color: roleChipColors.contrastText 1522 }, 1523 ...isNegotiating(contract.status) && contract.status !== contractStatusesValues().RECEIVER_REVIEW && contract.status !== contractStatusesValues().INITIATOR_REVIEW && { 1524 backgroundColor: "#fdcb6e", 1525 color: "#333" 1526 }, 1527 ...contract.status === contractStatusesValues().EXPIRED && { 1528 backgroundColor: "#efedee", 1529 color: "#333" 1530 } 1531 }; 1532 } 1533 }); 1534 } 1535 }), createComponent(TableCell, { 1536 get children() { 1537 return getCounterpartyLegalEntityName(contract); 1538 } 1539 }), createComponent(TableCell, { 1540 get children() { 1541 return createComponent(Show, { 1542 get when() { 1543 return isNegotiating(contract.status); 1544 }, 1545 fallback: "-", 1546 get children() { 1547 return createComponent(Typography, { 1548 component: "span", 1549 get children() { 1550 return contract.issues; 1551 } 1552 }); 1553 } 1554 }); 1555 } 1556 }), createComponent(TableCell, { 1557 get children() { 1558 return formatDate(contract.lastShared); 1559 } 1560 })]; 1561 } 1562 }) 1563 }); 1564 } 1565 })]; 1566 } 1567 }); 1568 } 1569 })]; 1570 } 1571 })]; 1572} 1573const creatableContractTypeKeys = FEATURE_CONSULTING_AGREEMENT ? contractTypes.keys : Object.fromEntries( 1574 Object.entries(contractTypes.keys).filter(([k]) => k !== "CONSULTING") 1575); 1576const createBlankTemplateSchema = object({ 1577 name: string(), 1578 contractType: enum_(creatableContractTypeKeys, "Please select a valid contract type") 1579}); 1580const CONTRACT_TYPES = [{ 1581 key: "NDA", 1582 value: "Non-Disclosure Agreement" 1583}, { 1584 key: "CONSULTING", 1585 value: "Consulting Services Agreement" 1586}]; 1587function CreateTemplateModal(props) { 1588 const navigate = useNavigate(); 1589 const toast = useToast(); 1590 const [contractType, setContractType] = createSignal(""); 1591 const [templateName, setTemplateName] = createSignal(""); 1592 const [isSubmitting, setIsSubmitting] = createSignal(false); 1593 const [validationError, setValidationError] = createSignal(""); 1594 const contractTypesMemo = createMemo(() => FEATURE_CONSULTING_AGREEMENT ? CONTRACT_TYPES : CONTRACT_TYPES.filter((t) => t.key !== "CONSULTING")); 1595 const resetForm = () => { 1596 setContractType(""); 1597 setTemplateName(""); 1598 setValidationError(""); 1599 setIsSubmitting(false); 1600 };
1601 createEffect(() => { 1602 if (!props.open) { 1603 resetForm(); 1604 } 1605 }); 1606 const handleDialogClose = () => { 1607 var _a; 1608 if (isSubmitting()) return; 1609 (_a = props.onClose) == null ? void 0 : _a.call(props); 1610 }; 1611 const handleSubmit = async (event) => { 1612 var _a; 1613 event.preventDefault(); 1614 setValidationError(""); 1615 try { 1616 parse(createBlankTemplateSchema, { 1617 name: templateName().trim(), 1618 contractType: contractType() 1619 }); 1620 } catch (error) { 1621 const message = error instanceof Error ? error.message : "Please fill in all required fields"; 1622 setValidationError(message); 1623 return; 1624 } 1625 if (!templateName().trim()) { 1626 setValidationError("Template name is required"); 1627 return; 1628 } 1629 setIsSubmitting(true); 1630 try { 1631 const { 1632 template: template2 1633 } = await apiClient.templates.createBlank({ 1634 name: templateName().trim(), 1635 contractType: contractType() 1636 }); 1637 (_a = props.onClose) == null ? void 0 : _a.call(props); 1638 navigate(`/playbook/template/${template2._id}`); 1639 } catch (error) { 1640 if (error instanceof ApiClientError && error.status === 401) { 1641 window.location.href = "/"; 1642 return; 1643 } 1644 const message = error instanceof ApiClientError ? error.message : error instanceof Error ? error.message : "Failed to create template"; 1645 toast.showToast("error", message, "Template Creation Error"); 1646 } finally { 1647 setIsSubmitting(false); 1648 } 1649 }; 1650 return createComponent(Dialog, { 1651 get open() { 1652 return props.open; 1653 }, 1654 onClose: handleDialogClose, 1655 maxWidth: "md", 1656 fullWidth: true, 1657 get children() { 1658 return createComponent(Box, { 1659 component: "form", 1660 onSubmit: (event) => void handleSubmit(event), 1661 get children() { 1662 return [createComponent(DialogTitle, { 1663 children: "Create New Template" 1664 }), createComponent(DialogContent, { 1665 sx: { 1666 paddingTop: "1rem" 1667 }, 1668 get children() { 1669 return [createComponent(Stack, { 1670 direction: { 1671 xs: "column", 1672 md: "row" 1673 }, 1674 spacing: 4, 1675 sx: { 1676 marginBottom: "1.5rem" 1677 }, 1678 get children() { 1679 return [createComponent(Box, { 1680 sx: { 1681 flex: 1 1682 }, 1683 get children() { 1684 return [createComponent(Typography, { 1685 variant: "h6", 1686 color: "text.primary", 1687 sx: { 1688 marginBottom: 1 1689 }, 1690 children: "Contract Type" 1691 }), createComponent(FormControl, { 1692 fullWidth: true, 1693 get children() { 1694 return [createComponent(InputLabel, { 1695 id: "template-contract-type-label", 1696 children: "Contract Type" 1697 }), createComponent(Select, { 1698 labelId: "template-contract-type-label", 1699 id: "template-contract-type-select", 1700 get value() { 1701 return contractType(); 1702 }, 1703 label: "Contract Type", 1704 onChange: (e) => setContractType(e.target.value), 1705 MenuProps: { 1706 disablePortal: true 1707 }, 1708 get children() { 1709 return contractTypesMemo().map(({ 1710 key, 1711 value 1712 }) => createComponent(MenuItem, { 1713 value: key, 1714 children: value 1715 })); 1716 } 1717 })]; 1718 } 1719 })]; 1720 } 1721 }), createComponent(Box, { 1722 sx: { 1723 flex: 1 1724 }, 1725 get children() { 1726 return [createComponent(Typography, { 1727 variant: "h6", 1728 color: "text.primary", 1729 sx: { 1730 marginBottom: 1 1731 }, 1732 children: "Template Name" 1733 }), createComponent(TextField, { 1734 fullWidth: true, 1735 placeholder: "Enter a template name", 1736 name: "templateName", 1737 type: "text", 1738 get value() { 1739 return templateName(); 1740 }, 1741 onChange: (e) => setTemplateName(e.target.value) 1742 })]; 1743 } 1744 })]; 1745 } 1746 }), createComponent(Show, { 1747 get when() { 1748 return validationError(); 1749 }, 1750 get children() { 1751 return createComponent(Typography, { 1752 variant: "body2", 1753 color: "error", 1754 get children() { 1755 return validationError(); 1756 } 1757 }); 1758 } 1759 })]; 1760 } 1761 }), createComponent(DialogActions, { 1762 sx: { 1763 padding: "1rem 1.5rem 1.5rem" 1764 }, 1765 get children() { 1766 return [createComponent(AppButton, { 1767 intent: "secondary", 1768 onClick: handleDialogClose, 1769 get disabled() { 1770 return isSubmitting(); 1771 }, 1772 children: "Cancel" 1773 }), createComponent(AppButton, { 1774 intent: "primary", 1775 type: "submit", 1776 get disabled() { 1777 return isSubmitting(); 1778 }, 1779 get children() { 1780 return isSubmitting() ? "Creatingâ¦" : "Create Template"; 1781 } 1782 })]; 1783 } 1784 })]; 1785 } 1786 }); 1787 } 1788 }); 1789} 1790var _tmpl$ = /* @__PURE__ */ template(`<a href=# class=light>`); 1791function Templates({ 1792 templates, 1793 paginationInfo, 1794 onPageChange, 1795 onRefresh 1796}) { 1797 const navigate = useNavigate(); 1798 const toast = useToast(); 1799 onMount(() => { 1800 performanceLogger.logComponentMount("Templates"); 1801 performanceLogger.startComponentLoad("Templates", "page"); 1802 });
1803 createEffect(() => { 1804 if (templates() && templates().length >= 0) { 1805 performanceLogger.endComponentLoad("Templates"); 1806 } 1807 }); 1808 const handlePageChange = (_event, page) => { 1809 onPageChange(page); 1810 }; 1811 const [anchorEl, setAnchorEl] = createSignal(null); 1812 const [selectedTemplate, setSelectedTemplate] = createSignal(null); 1813 const [searchTerm, setSearchTerm] = createSignal(""); 1814 const [sortColumn, setSortColumn] = createSignal(null); 1815 const [sortDirection, setSortDirection] = createSignal("asc"); 1816 const [deleteConfirmOpen, setDeleteConfirmOpen] = createSignal(false); 1817 const [deletingTemplate, setDeletingTemplate] = createSignal(null); 1818 const [isDeleting, setIsDeleting] = createSignal(false); 1819 const [duplicateModalOpen, setDuplicateModalOpen] = createSignal(false); 1820 const [duplicatingTemplate, setDuplicatingTemplate] = createSignal(null); 1821 const [duplicateNameInput, setDuplicateNameInput] = createSignal(""); 1822 const [isDuplicating, setIsDuplicating] = createSignal(false); 1823 const [renameModalOpen, setRenameModalOpen] = createSignal(false); 1824 const [renamingTemplate, setRenamingTemplate] = createSignal(null); 1825 const [renameNameInput, setRenameNameInput] = createSignal(""); 1826 const [createTemplateModalOpen, setCreateTemplateModalOpen] = createSignal(false); 1827 const [createTemplateTooltipAnchor, setCreateTemplateTooltipAnchor] = createSignal(null); 1828 const [isRenaming, setIsRenaming] = createSignal(false); 1829 const isValidTooltipAnchor = (anchor) => anchor && document.body.contains(anchor) ? anchor : null; 1830 const validCreateTemplateTooltipAnchor = () => isValidTooltipAnchor(createTemplateTooltipAnchor()); 1831 const formatDate = (date) => { 1832 if (!date) return "-"; 1833 const d = new Date(date); 1834 return d.toLocaleDateString("en-US", { 1835 year: "numeric", 1836 month: "2-digit", 1837 day: "2-digit" 1838 }); 1839 }; 1840 const handleMenuClick = (event, template2) => { 1841 setAnchorEl(event.currentTarget); 1842 setSelectedTemplate(template2); 1843 }; 1844 const handleMenuClose = () => { 1845 setAnchorEl(null); 1846 setSelectedTemplate(null); 1847 }; 1848 const handleEditTemplate = (template2) => { 1849 handleMenuClose(); 1850 navigate(`/playbook/template/${template2._id}`); 1851 }; 1852 const handleOpenTemplate = (template2) => { 1853 handleMenuClose(); 1854 navigate(`/create-contract/from-template/${template2._id}`); 1855 }; 1856 const handleTemplateNameClick = (template2, event) => { 1857 event.preventDefault(); 1858 navigate(`/create-contract/from-template/${template2._id}`); 1859 }; 1860 const handleDeleteClick = (template2) => { 1861 if (document.activeElement instanceof HTMLElement) { 1862 document.activeElement.blur(); 1863 } 1864 handleMenuClose(); 1865 setDeletingTemplate(template2); 1866 requestAnimationFrame(() => { 1867 setDeleteConfirmOpen(true); 1868 }); 1869 }; 1870 const handleDeleteConfirm = async () => { 1871 const template2 = deletingTemplate(); 1872 if (!template2) return; 1873 setIsDeleting(true); 1874 try { 1875 const response = await fetchAPIDelete(`/api/templates/${template2._id}`); 1876 if (!response.ok) { 1877 if (response.status === 401) { 1878 window.location.href = "/"; 1879 return; 1880 } 1881 const errorData = await response.json().catch(() => ({ 1882 error: "Failed to delete template" 1883 })); 1884 throw new Error(errorData.error || "Failed to delete template"); 1885 } 1886 toast.showToast("success", "Template deleted successfully"); 1887 setDeleteConfirmOpen(false); 1888 setDeletingTemplate(null); 1889 if (onRefresh) { 1890 await onRefresh(); 1891 } 1892 } catch (error) { 1893 toast.showToast("error", error instanceof Error ? error.message : "Failed to delete template"); 1894 } finally { 1895 setIsDeleting(false); 1896 } 1897 }; 1898 const handleDeleteCancel = () => { 1899 setDeleteConfirmOpen(false); 1900 setDeletingTemplate(null); 1901 }; 1902 const handleDuplicateClick = (template2) => { 1903 if (document.activeElement instanceof HTMLElement) { 1904 document.activeElement.blur(); 1905 } 1906 handleMenuClose(); 1907 setDuplicatingTemplate(template2); 1908 setDuplicateNameInput(`Copy of ${template2.name}`); 1909 requestAnimationFrame(() => { 1910 setDuplicateModalOpen(true); 1911 }); 1912 }; 1913 const handleRenameClick = (template2) => { 1914 if (document.activeElement instanceof HTMLElement) { 1915 document.activeElement.blur(); 1916 } 1917 handleMenuClose(); 1918 setRenamingTemplate(template2); 1919 setRenameNameInput(template2.name); 1920 requestAnimationFrame(() => { 1921 setRenameModalOpen(true); 1922 }); 1923 }; 1924 const handleDuplicateCancel = () => { 1925 setDuplicateModalOpen(false); 1926 setDuplicatingTemplate(null); 1927 setDuplicateNameInput(""); 1928 }; 1929 const handleDuplicateSubmit = async () => { 1930 const template2 = duplicatingTemplate(); 1931 const name = duplicateNameInput().trim(); 1932 if (!template2 || !name) { 1933 return; 1934 } 1935 setIsDuplicating(true); 1936 try { 1937 const response = await fetchAPIPost(`/api/templates/${template2._id}/duplicate`, { 1938 name 1939 }); 1940 if (!response.ok) { 1941 if (response.status === 401) { 1942 window.location.href = "/"; 1943 return; 1944 } 1945 const errorData = await response.json().catch(() => ({ 1946 error: "Failed to duplicate template" 1947 })); 1948 throw new Error(errorData.error || "Failed to duplicate template"); 1949 } 1950 toast.showToast("success", `Template "${name}" created successfully`); 1951 setDuplicateModalOpen(false); 1952 setDuplicatingTemplate(null); 1953 setDuplicateNameInput(""); 1954 if (onRefresh) { 1955 await onRefresh(); 1956 } 1957 } catch (error) { 1958 toast.showToast("error", error instanceof Error ? error.message : "Failed to duplicate template"); 1959 } finally { 1960 setIsDuplicating(false); 1961 } 1962 }; 1963 const handleRenameCancel = () => { 1964 setRenameModalOpen(false); 1965 setRenamingTemplate(null); 1966 setRenameNameInput(""); 1967 }; 1968 const handleRenameSubmit = async () => { 1969 const template2 = renamingTemplate(); 1970 const name = renameNameInput().trim(); 1971 if (!template2 || !name) { 1972 return; 1973 } 1974 setIsRenaming(true); 1975 try { 1976 const response = await fetchAPIPut(`/api/templates/${template2._id}`, { 1977 name 1978 }); 1979 if (!response.ok) { 1980 if (response.status === 401) { 1981 window.location.href = "/"; 1982 return; 1983 } 1984 const errorData = await response.json().catch(() => ({ 1985 error: "Failed to rename template" 1986 })); 1987 throw new Error(errorData.error || "Failed to rename template"); 1988 } 1989 toast.showToast("success", `Template renamed to "${name}"`); 1990 setRenameModalOpen(false); 1991 setRenamingTemplate(null); 1992 setRenameNameInput(""); 1993 if (onRefresh) { 1994 await onRefresh(); 1995 } 1996 } catch (error) { 1997 toast.showToast("error", error instanceof Error ? error.message : "Failed to rename template"); 1998 } finally { 1999 setIsRenaming(false); 2000 } 2001 }; 2002 const handleSort = (column) => { 2003 if (sortColumn() === column) { 2004 setSortDirection(sortDirection() === "asc" ? "desc" : "asc"); 2005 } else { 2006 setSortColumn(column); 2007 setSortDirection("asc"); 2008 } 2009 }; 2010 const filteredTemplates = createMemo(() => { 2011 const templatesData = templates(); 2012 if (!templatesData) return []; 2013 if (!searchTerm()) return templatesData; 2014 const term = searchTerm().toLowerCase(); 2015 return templatesData.filter((template2) => { 2016 var _a, _b; 2017 const name = ((_a = template2.name) == null ? void 0 : _a.toLowerCase()) || ""; 2018 const type = ((_b = template2.type) == null ? void 0 : _b.toLowerCase()) || ""; 2019 return name.includes(term) || type.includes(term); 2020 }); 2021 }); 2022 const sortedTemplates = createMemo(() => { 2023 const filtered = filteredTemplates(); 2024 if (!sortColumn()) return filtered; 2025 const sorted = [...filtered].sort((a, b) => { 2026 let aVal; 2027 let bVal; 2028 switch (sortColumn()) { 2029 case "type": 2030 aVal = a.type || ""; 2031 bVal = b.type || ""; 2032 break; 2033 case "name": 2034 aVal = a.name || ""; 2035 bVal = b.name || ""; 2036 break; 2037 case "createdAt": 2038 aVal = a.createdAt ? new Date(a.createdAt).getTime() : 0; 2039 bVal = b.createdAt ? new Date(b.createdAt).getTime() : 0; 2040 break; 2041 default: 2042 return 0; 2043 } 2044 if (sortColumn() === "createdAt") { 2045 return sortDirection() === "asc" ? aVal - bVal : bVal - aVal; 2046 } 2047 const aStr = String(aVal).toLowerCase(); 2048 const bStr = String(bVal).toLowerCase(); 2049 if (aStr < bStr) return sortDirection() === "asc" ? -1 : 1; 2050 if (aStr > bStr) return sortDirection() === "asc" ? 1 : -1; 2051 return 0; 2052 }); 2053 return sorted; 2054 }); 2055 return [createComponent(Title, { 2056 children: "Templates" 2057 }), createComponent(Box, { 2058 sx: { 2059 width: "100%", 2060 height: "100%", 2061 display: "flex", 2062 flexDirection: "column" 2063 }, 2064 get children() { 2065 return [createComponent(Box, { 2066 sx: { 2067 display: "flex", 2068 justifyContent: "space-between", 2069 alignItems: "center", 2070 marginBottom: "1rem", 2071 flexShrink: 0, 2072 gap: "1rem" 2073 }, 2074 get children() { 2075 return [createComponent(NoHydration, { 2076 get children() { 2077 return createComponent(TextField, { 2078 placeholder: "Search templates...", 2079 get value() { 2080 return searchTerm(); 2081 }, 2082 onChange: (e) => setSearchTerm(e.target.value), 2083 sx: { 2084 maxWidth: "400px", 2085 "& .MuiInputBase-input::placeholder": { 2086 color: "primary.light", 2087 opacity: 1 2088 }, 2089 "& .MuiInputBase-input": { 2090 color: "black" 2091 } 2092 }, 2093 size: "small", 2094 get InputProps() { 2095 return { 2096 startAdornment: createComponent(InputAdornment, { 2097 position: "start", 2098 get children() { 2099 return createComponent(Search, { 2100 color: "primary" 2101 }); 2102 } 2103 }) 2104 }; 2105 } 2106 }); 2107 } 2108 }), createComponent(Box, { 2109 component: "span", 2110 "data-testid": "create-new-template-tooltip-anchor", 2111 onMouseEnter: (e) => setCreateTemplateTooltipAnchor(e.currentTarget), 2112 onMouseLeave: () => setCreateTemplateTooltipAnchor(null), 2113 sx: { 2114 display: "inline-block" 2115 }, 2116 get children() { 2117 return [createComponent(AppButton, { 2118 intent: "primary", 2119 get startIcon() { 2120 return createComponent(Add, {}); 2121 }, 2122 onClick: () => setCreateTemplateModalOpen(true),
2123 "data-testid": "create-new-template-button", 2124 children: "Create New Template" 2125 }), createComponent(Popover, { 2126 get open() { 2127 return !!validCreateTemplateTooltipAnchor(); 2128 }, 2129 get anchorEl() { 2130 return validCreateTemplateTooltipAnchor() ?? void 0; 2131 }, 2132 onClose: () => setCreateTemplateTooltipAnchor(null), 2133 anchorOrigin: { 2134 vertical: "top", 2135 horizontal: "center" 2136 }, 2137 transformOrigin: { 2138 vertical: "bottom", 2139 horizontal: "center" 2140 }, 2141 disableRestoreFocus: true, 2142 sx: { 2143 pointerEvents: "none" 2144 }, 2145 get children() { 2146 return createComponent(Typography, { 2147 variant: "body2", 2148 sx: { 2149 padding: "0.75rem", 2150 maxWidth: "300px" 2151 }, 2152 children: "Create a reusable playbook you can use to start new contracts." 2153 }); 2154 } 2155 })]; 2156 } 2157 })]; 2158 } 2159 }), createComponent(TableContainer, { 2160 component: Paper, 2161 sx: { 2162 flexGrow: 1, 2163 overflow: "auto" 2164 }, 2165 get children() { 2166 return createComponent(Table, { 2167 get children() { 2168 return [createComponent(TableHead, { 2169 get children() { 2170 return createComponent(TableRow, { 2171 get children() { 2172 return [createComponent(TableCell, { 2173 sx: { 2174 width: "1.8rem" 2175 } 2176 }), createComponent(TableCell, { 2177 sx: { 2178 cursor: "pointer", 2179 userSelect: "none" 2180 }, 2181 onClick: () => handleSort("type"), 2182 get children() { 2183 return createComponent(Box, { 2184 sx: { 2185 display: "flex", 2186 alignItems: "center", 2187 gap: "0.5rem" 2188 }, 2189 get children() { 2190 return ["Type", createComponent(Box, { 2191 get sx() { 2192 return { 2193 display: "flex", 2194 flexDirection: "column", 2195 opacity: sortColumn() === "type" ? 1 : 0.3 2196 }; 2197 }, 2198 get children() { 2199 return [createComponent(ArrowDropUp, { 2200 get sx() { 2201 return { 2202 fontSize: "0.875rem", 2203 opacity: sortColumn() === "type" && sortDirection() === "asc" ? 1 : 0.3, 2204 transition: "opacity 0.2s" 2205 }; 2206 } 2207 }), createComponent(ArrowDropDown, { 2208 get sx() { 2209 return { 2210 fontSize: "0.875rem", 2211 marginTop: "-0.25rem", 2212 opacity: sortColumn() === "type" && sortDirection() === "desc" ? 1 : 0.3, 2213 transition: "opacity 0.2s" 2214 }; 2215 } 2216 })]; 2217 } 2218 })]; 2219 } 2220 }); 2221 } 2222 }), createComponent(TableCell, { 2223 sx: { 2224 cursor: "pointer", 2225 userSelect: "none" 2226 }, 2227 onClick: () => handleSort("name"), 2228 get children() { 2229 return createComponent(Box, { 2230 sx: { 2231 display: "flex", 2232 alignItems: "center", 2233 gap: "0.5rem" 2234 }, 2235 get children() { 2236 return ["Name", createComponent(Box, { 2237 get sx() { 2238 return { 2239 display: "flex", 2240 flexDirection: "column",
2241 opacity: sortColumn() === "name" ? 1 : 0.3 2242 }; 2243 }, 2244 get children() { 2245 return [createComponent(ArrowDropUp, { 2246 get sx() { 2247 return { 2248 fontSize: "0.875rem", 2249 opacity: sortColumn() === "name" && sortDirection() === "asc" ? 1 : 0.3, 2250 transition: "opacity 0.2s" 2251 }; 2252 } 2253 }), createComponent(ArrowDropDown, { 2254 get sx() { 2255 return { 2256 fontSize: "0.875rem", 2257 marginTop: "-0.25rem", 2258 opacity: sortColumn() === "name" && sortDirection() === "desc" ? 1 : 0.3, 2259 transition: "opacity 0.2s" 2260 }; 2261 } 2262 })]; 2263 } 2264 })]; 2265 } 2266 }); 2267 } 2268 }), createComponent(TableCell, { 2269 sx: { 2270 cursor: "pointer", 2271 userSelect: "none" 2272 }, 2273 onClick: () => handleSort("createdAt"), 2274 get children() { 2275 return createComponent(Box, { 2276 sx: { 2277 display: "flex", 2278 alignItems: "center", 2279 gap: "0.5rem" 2280 }, 2281 get children() { 2282 return ["Created At", createComponent(Box, { 2283 get sx() { 2284 return { 2285 display: "flex", 2286 flexDirection: "column", 2287 opacity: sortColumn() === "createdAt" ? 1 : 0.3 2288 }; 2289 }, 2290 get children() { 2291 return [createComponent(ArrowDropUp, { 2292 get sx() { 2293 return { 2294 fontSize: "0.875rem", 2295 opacity: sortColumn() === "createdAt" && sortDirection() === "asc" ? 1 : 0.3, 2296 transition: "opacity 0.2s" 2297 }; 2298 } 2299 }), createComponent(ArrowDropDown, { 2300 get sx() { 2301 return { 2302 fontSize: "0.875rem", 2303 marginTop: "-0.25rem", 2304 opacity: sortColumn() === "createdAt" && sortDirection() === "desc" ? 1 : 0.3, 2305 transition: "opacity 0.2s" 2306 }; 2307 } 2308 })]; 2309 } 2310 })]; 2311 } 2312 }); 2313 } 2314 })]; 2315 } 2316 }); 2317 } 2318 }), createComponent(TableBody, { 2319 get children() { 2320 return createComponent(For, { 2321 get each() { 2322 return sortedTemplates(); 2323 }, 2324 children: (template2) => createComponent(TableRow, { 2325 get children() { 2326 return [createComponent(TableCell, { 2327 get children() { 2328 return [createComponent(AppIconButton, { 2329 onClick: (e) => handleMenuClick(e, template2), 2330 get children() { 2331 return createComponent(MoreVert, { 2332 color: "primary" 2333 }); 2334 } 2335 }), createComponent(Menu, { 2336 get anchorEl() { 2337 return anchorEl(); 2338 }, 2339 get open() { 2340 var _a; 2341 return memo(() => !!Boolean(anchorEl()))() && ((_a = selectedTemplate()) == null ? void 0 : _a._id) === template2._id; 2342 }, 2343 onClose: handleMenuClose, 2344 anchorOrigin: { 2345 vertical: "bottom", 2346 horizontal: "left" 2347 }, 2348 transformOrigin: { 2349 vertical: "top", 2350 horizontal: "left" 2351 }, 2352 get children() { 2353 return [createComponent(MenuItem, { 2354 onClick: () => handleOpenTemplate(template2), 2355 "data-testid": "template-menu-create-contract", 2356 children: "Create Contract" 2357 }), createComponent(MenuItem, { 2358 onClick: () => handleEditTemplate(template2), 2359 children: "Edit" 2360 }), createComponent(MenuItem, { 2361 onClick: () => handleRenameClick(template2), 2362 children: "Rename" 2363 }), createComponent(MenuItem, { 2364 onClick: () => handleDuplicateClick(template2), 2365 children: "Duplicate" 2366 }), createComponent(MenuItem, { 2367 onClick: () => handleDeleteClick(template2), 2368 children: "Delete" 2369 })]; 2370 } 2371 })]; 2372 } 2373 }), createComponent(TableCell, { 2374 sx: { 2375 fontWeight: 600 2376 }, 2377 get children() { 2378 return template2.type; 2379 } 2380 }), createComponent(TableCell, { 2381 get children() { 2382 var _el$ = _tmpl$(); 2383 _el$.$$click = (e) => handleTemplateNameClick(template2, e); 2384 insert(_el$, () => template2.name); 2385 return _el$; 2386 } 2387 }), createComponent(TableCell, { 2388 get children() { 2389 return memo(() => !!template2.createdAt)() ? formatDate(template2.createdAt) : "-"; 2390 } 2391 })]; 2392 } 2393 }) 2394 }); 2395 } 2396 })]; 2397 } 2398 }); 2399 } 2400 }), createComponent(Show, { 2401 get when() { 2402 return memo(() => !!paginationInfo())() && paginationInfo().totalPages > 1; 2403 }, 2404 get children() { 2405 return createComponent(Box, { 2406 sx: { 2407 display: "flex", 2408 justifyContent: "center", 2409 padding: "1rem", 2410 flexShrink: 0 2411 }, 2412 get children() { 2413 return createComponent(Pagination, { 2414 get count() { 2415 return paginationInfo().totalPages; 2416 }, 2417 get page() { 2418 return paginationInfo().currentPage; 2419 }, 2420 onChange: handlePageChange, 2421 color: "primary", 2422 showFirstButton: true, 2423 showLastButton: true 2424 }); 2425 } 2426 }); 2427 } 2428 })]; 2429 } 2430 }), createComponent(Dialog, { 2431 get open() { 2432 return deleteConfirmOpen(); 2433 },
2434 onClose: handleDeleteCancel, 2435 maxWidth: "sm", 2436 fullWidth: true, 2437 get children() { 2438 return [createComponent(DialogTitle, { 2439 get children() { 2440 return createComponent(Typography, { 2441 variant: "h6", 2442 children: "Confirm Delete" 2443 }); 2444 } 2445 }), createComponent(DialogContent, { 2446 get children() { 2447 return createComponent(Typography, { 2448 variant: "body1", 2449 get children() { 2450 return ['Are you sure you want to delete the template "', memo(() => { 2451 var _a; 2452 return (_a = deletingTemplate()) == null ? void 0 : _a.name; 2453 }), '"? This action cannot be undone.']; 2454 } 2455 }); 2456 } 2457 }), createComponent(DialogActions, { 2458 get children() { 2459 return [createComponent(AppButton, { 2460 intent: "secondary", 2461 onClick: handleDeleteCancel, 2462 get disabled() { 2463 return isDeleting(); 2464 }, 2465 children: "Cancel" 2466 }), createComponent(AppButton, { 2467 intent: "destructive", 2468 onClick: handleDeleteConfirm, 2469 get disabled() { 2470 return isDeleting(); 2471 }, 2472 get children() { 2473 return isDeleting() ? "Deleting..." : "Delete"; 2474 } 2475 })]; 2476 } 2477 })]; 2478 } 2479 }), createComponent(Dialog, { 2480 get open() { 2481 return duplicateModalOpen(); 2482 }, 2483 onClose: handleDuplicateCancel, 2484 maxWidth: "sm", 2485 fullWidth: true, 2486 get children() { 2487 return [createComponent(DialogTitle, { 2488 children: "Duplicate Template" 2489 }), createComponent(DialogContent, { 2490 get children() { 2491 return [createComponent(DialogContentText, { 2492 sx: { 2493 mb: 2 2494 }, 2495 children: "Enter a name for the duplicated template:" 2496 }), createComponent(TextField, { 2497 autoFocus: true, 2498 margin: "dense", 2499 label: "Template Name", 2500 type: "text", 2501 fullWidth: true, 2502 variant: "outlined", 2503 get value() { 2504 return duplicateNameInput(); 2505 }, 2506 onChange: (e) => setDuplicateNameInput(e.target.value), 2507 onKeyDown: (e) => { 2508 if (e.key === "Enter" && !e.shiftKey) { 2509 e.preventDefault(); 2510 handleDuplicateSubmit(); 2511 } 2512 } 2513 })]; 2514 } 2515 }), createComponent(DialogActions, { 2516 get children() { 2517 return [createComponent(AppButton, { 2518 intent: "secondary", 2519 onClick: handleDuplicateCancel, 2520 get disabled() { 2521 return isDuplicating(); 2522 }, 2523 children: "Cancel" 2524 }), createComponent(AppButton, { 2525 intent: "primary", 2526 onClick: handleDuplicateSubmit, 2527 get disabled() { 2528 return !duplicateNameInput().trim() || isDuplicating(); 2529 }, 2530 get children() { 2531 return isDuplicating() ? "Duplicating..." : "Duplicate Template"; 2532 } 2533 })]; 2534 } 2535 })]; 2536 } 2537 }), createComponent(Dialog, { 2538 get open() { 2539 return renameModalOpen(); 2540 }, 2541 onClose: handleRenameCancel, 2542 maxWidth: "sm", 2543 fullWidth: true, 2544 get children() { 2545 return [createComponent(DialogTitle, { 2546 children: "Rename Template" 2547 }), createComponent(DialogContent, { 2548 get children() { 2549 return [createComponent(DialogContentText, { 2550 sx: { 2551 mb: 2 2552 }, 2553 children: "Enter a new name for the template:" 2554 }), createComponent(TextField, { 2555 autoFocus: true, 2556 margin: "dense", 2557 label: "Template Name", 2558 type: "text", 2559 fullWidth: true, 2560 variant: "outlined", 2561 get value() { 2562 return renameNameInput(); 2563 }, 2564 onChange: (e) => setRenameNameInput(e.target.value), 2565 onKeyDown: (e) => { 2566 if (e.key === "Enter" && !e.shiftKey) { 2567 e.preventDefault(); 2568 handleRenameSubmit(); 2569 } 2570 } 2571 })]; 2572 } 2573 }), createComponent(DialogActions, { 2574 get children() { 2575 return [createComponent(AppButton, { 2576 intent: "secondary", 2577 onClick: handleRenameCancel, 2578 get disabled() { 2579 return isRenaming(); 2580 }, 2581 children: "Cancel" 2582 }), createComponent(AppButton, { 2583 intent: "primary", 2584 onClick: handleRenameSubmit, 2585 get disabled() { 2586 return !renameNameInput().trim() || isRenaming(); 2587 }, 2588 get children() { 2589 return isRenaming() ? "Renaming..." : "Rename Template"; 2590 } 2591 })]; 2592 } 2593 })]; 2594 } 2595 }), createComponent(CreateTemplateModal, { 2596 get open() { 2597 return createTemplateModalOpen(); 2598 }, 2599 onClose: () => setCreateTemplateModalOpen(false) 2600 })]; 2601} 2602delegateEvents(["click"]); 2603const DASHBOARD_FIRST_USE_PAGES = [{ 2604 title: "This is your dashboard.", 2605 description: "Everything starts here. The Contracts tab shows your active and completed agreements. The Templates tab is where youâll find reusable playbooks you create that power those agreements." 2606}, { 2607 title: "Starting a contract is one click.", 2608 image: createHelpS3Url("Dashboard2.jpg"), 2609 video: createHelpS3Url("Dashboard2.mp4"), 2610 description: 'Hit "Create New Contract" to begin. Youâll choose a product (type of contract), fill in the basics, and step into a workspace where the agreement actually takes shape. No blank pages, no guesswork.' 2611}, { 2612 title: "A few things worth knowing.", 2613 image: createHelpS3Url("Dashboard1.jpg"), 2614 video: createHelpS3Url("Dashboard1.mp4"), 2615 description: "You can track every agreement from this view, whether itâs in progress, waiting on the other side, or fully signed. Filters and search keep things manageable as your volume grows.\n\nYou can always come back here by clicking the Transactency logo.", 2616 ctaText: "Got it!", 2617 ctaClosesModal: true 2618}]; 2619function Dashboard(props) { 2620 const [renderReady, setRenderReady] = createSignal(false); 2621 onMount(() => { 2622 performanceLogger.logComponentMount("Dashboard"); 2623 setTimeout(() => { 2624 setRenderReady(true); 2625 }, 100); 2626 checkFirstUseModalSeen("dashboard").then((seen) => { 2627 if (!seen) { 2628 setFirstUseModalOpen(true); 2629 } 2630 }); 2631 }); 2632 const location = useLocation(); 2633 const getInitialTab = () => { 2634 return location.pathname.includes("/templates") ? "templates" : "contracts"; 2635 }; 2636 const [activeTab, setActiveTab] = createSignal(getInitialTab()); 2637 const [firstUseModalOpen, setFirstUseModalOpen] = createSignal(false); 2638 const handleFirstUseModalClose = () => { 2639 setFirstUseModalOpen(false); 2640 markFirstUseModalSeen("dashboard"); 2641 }; 2642 const handleTabChange = (tab) => { 2643 setActiveTab(tab); 2644 const newUrl = tab === "contracts" ? "/contracts" : "/templates"; 2645 window.history.replaceState(null, "", newUrl); 2646 }; 2647 return createComponent(Box, { 2648 get children() { 2649 return createComponent(Show, { 2650 get when() { 2651 return renderReady(); 2652 }, 2653 get fallback() { 2654 return createComponent(Box, { 2655 sx: { 2656 display: "flex", 2657 justifyContent: "center", 2658 alignItems: "center", 2659 minHeight: "100vh" 2660 }, 2661 get children() { 2662 return [createComponent(CircularProgress, { 2663 size: 24, 2664 sx: { 2665 color: "#ecb34e" 2666 } 2667 }), createComponent(Box, { 2668 sx: { 2669 width: "1rem" 2670 } 2671 }), createComponent(Typography, { 2672 variant: "h3", 2673 color: "text.white", 2674 children: "Loading..." 2675 })]; 2676 } 2677 }); 2678 }, 2679 get children() { 2680 return [createComponent(Nav, { 2681 get user() { 2682 return props.user; 2683 }, 2684 title: "" 2685 }), createComponent(Container, { 2686 maxWidth: "lg", 2687 sx: { 2688 padding: "2rem 0" 2689 }, 2690 get children() {
2691 return createComponent(Suspense, { 2692 get fallback() { 2693 return createComponent(Box, { 2694 sx: { 2695 display: "flex", 2696 justifyContent: "center", 2697 alignItems: "center", 2698 padding: "4rem 0" 2699 }, 2700 get children() { 2701 return createComponent(CircularProgress, { 2702 size: 24, 2703 sx: { 2704 color: "#ecb34e" 2705 } 2706 }); 2707 } 2708 }); 2709 }, 2710 get children() { 2711 return [memo(() => memo(() => !!props.user())() && createComponent(Typography, { 2712 variant: "h3", 2713 color: "text.white", 2714 sx: { 2715 marginBottom: "1rem" 2716 }, 2717 get children() { 2718 return [memo(() => { 2719 var _a; 2720 return (_a = props.user()) == null ? void 0 : _a.name; 2721 }), "'s Dashboard"]; 2722 } 2723 })), createComponent(Box, { 2724 sx: { 2725 padding: "0", 2726 marginBottom: "0", 2727 borderRadius: "8px 8px 0 0" 2728 }, 2729 get children() { 2730 return createComponent(Box, { 2731 sx: { 2732 display: "flex", 2733 padding: "0.5rem 0 0 0" 2734 }, 2735 get children() { 2736 return [createComponent(AppButton, { 2737 intent: "custom", 2738 onClick: () => handleTabChange("contracts"), 2739 variant: "text", 2740 get startIcon() { 2741 return createComponent(Assignment, {}); 2742 }, 2743 get sx() { 2744 return { 2745 backgroundColor: activeTab() === "contracts" ? "background.paper" : "primary.light", 2746 color: activeTab() === "contracts" ? "primary.main" : "primary.contrastText", 2747 borderRadius: "8px 8px 0 0", 2748 borderBottom: activeTab() === "contracts" ? "none" : "2px solid transparent", 2749 marginRight: "0.5rem", 2750 padding: "0.75rem 1.5rem", 2751 minWidth: "120px", 2752 "&:hover": { 2753 backgroundColor: activeTab() === "contracts" ? "background.paper" : "rgba(255, 255, 255, 0.1)" 2754 }, 2755 "&.Mui-disabled": { 2756 backgroundColor: activeTab() === "contracts" ? "background.paper" : "primary.light", 2757 color: activeTab() === "contracts" ? "primary.main" : "primary.light" 2758 } 2759 }; 2760 }, 2761 children: "Contracts" 2762 }), createComponent(AppButton, { 2763 intent: "custom", 2764 onClick: () => handleTabChange("templates"), 2765 variant: "text", 2766 get startIcon() { 2767 return createComponent(Description, {}); 2768 }, 2769 get sx() { 2770 return { 2771 backgroundColor: activeTab() === "templates" ? "background.paper" : "primary.light", 2772 color: activeTab() === "templates" ? "primary.main" : "primary.contrastText", 2773 borderRadius: "8px 8px 0 0", 2774 borderBottom: activeTab() === "templates" ? "none" : "2px solid transparent", 2775 padding: "0.75rem 1.5rem", 2776 minWidth: "120px", 2777 "&:hover": { 2778 backgroundColor: activeTab() === "templates" ? "background.paper" : "rgba(255, 255, 255, 0.1)" 2779 }, 2780 "&.Mui-disabled": { 2781 backgroundColor: activeTab() === "templates" ? "background.paper" : "primary.light", 2782 color: activeTab() === "templates" ? "primary.main" : "primary.light" 2783 } 2784 }; 2785 }, 2786 children: "Templates" 2787 })]; 2788 } 2789 }); 2790 } 2791 }), createComponent(Box, { 2792 sx: { 2793 backgroundColor: "background.paper", 2794 padding: "2rem", 2795 height: "calc(100vh - 240px)", 2796 maxHeight: "calc(100vh - 240px)", 2797 display: "flex", 2798 flexDirection: "column", 2799 overflow: "hidden" 2800 }, 2801 get children() { 2802 return [createComponent(Show, { 2803 get when() { 2804 return activeTab() === "contracts"; 2805 }, 2806 get children() {
2807 return createComponent(Suspense, { 2808 get fallback() { 2809 return createComponent(Box, { 2810 sx: { 2811 display: "flex", 2812 justifyContent: "center", 2813 padding: "2rem" 2814 }, 2815 get children() { 2816 return createComponent(CircularProgress, { 2817 size: 24, 2818 sx: { 2819 color: "#ecb34e" 2820 } 2821 }); 2822 } 2823 }); 2824 }, 2825 get children() { 2826 return createComponent(Contracts, { 2827 contracts: () => { 2828 var _a; 2829 return (_a = props.contracts()) == null ? void 0 : _a.contracts; 2830 }, 2831 paginationInfo: () => props.contracts() ? { 2832 totalCount: props.contracts().totalCount, 2833 totalPages: props.contracts().totalPages, 2834 currentPage: props.contracts().currentPage, 2835 pageSize: props.contracts().pageSize 2836 } : void 0, 2837 get onPageChange() { 2838 return props.onContractsPageChange; 2839 }, 2840 get includeArchived() { 2841 return props.includeArchived; 2842 }, 2843 get onToggleArchived() { 2844 return props.onToggleArchived; 2845 }, 2846 get onRefreshContracts() { 2847 return props.onRefreshContracts; 2848 } 2849 }); 2850 } 2851 }); 2852 } 2853 }), createComponent(Show, { 2854 get when() { 2855 return activeTab() === "templates"; 2856 }, 2857 get children() { 2858 return createComponent(Suspense, { 2859 get fallback() { 2860 return createComponent(Box, { 2861 sx: { 2862 display: "flex", 2863 justifyContent: "center", 2864 padding: "2rem" 2865 }, 2866 get children() { 2867 return createComponent(CircularProgress, { 2868 size: 24, 2869 sx: { 2870 color: "#ecb34e" 2871 } 2872 }); 2873 } 2874 }); 2875 }, 2876 get children() { 2877 return createComponent(Templates, { 2878 templates: () => { 2879 var _a; 2880 return (_a = props.templates()) == null ? void 0 : _a.templates; 2881 }, 2882 paginationInfo: () => props.templates() ? { 2883 totalCount: props.templates().totalCount, 2884 totalPages: props.templates().totalPages, 2885 currentPage: props.templates().currentPage, 2886 pageSize: props.templates().pageSize 2887 } : void 0, 2888 get onPageChange() { 2889 return props.onTemplatesPageChange; 2890 }, 2891 get onRefresh() { 2892 return props.onTemplatesRefresh; 2893 } 2894 }); 2895 } 2896 }); 2897 } 2898 })]; 2899 } 2900 })]; 2901 } 2902 }); 2903 } 2904 }), createComponent(FirstUseModal, { 2905 open: firstUseModalOpen, 2906 onClose: handleFirstUseModalClose, 2907 pageId: "dashboard", 2908 pages: DASHBOARD_FIRST_USE_PAGES 2909 }), createComponent(BindDropdowns, {})]; 2910 } 2911 }); 2912 } 2913 }); 2914} 2915export { 2916 Dashboard as D 2917};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.