@trustless-work/blocks 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +39 -13
  2. package/bin/index.js +1596 -1137
  3. package/package.json +44 -44
  4. package/templates/escrows/details/Actions.tsx +144 -149
  5. package/templates/escrows/details/Entities.tsx +1 -1
  6. package/templates/escrows/details/EntityCard.tsx +1 -3
  7. package/templates/escrows/details/EscrowDetailDialog.tsx +18 -18
  8. package/templates/escrows/details/GeneralInformation.tsx +20 -23
  9. package/templates/escrows/details/MilestoneCard.tsx +46 -47
  10. package/templates/escrows/details/MilestoneDetailDialog.tsx +1 -2
  11. package/templates/escrows/details/Milestones.tsx +0 -5
  12. package/templates/escrows/details/SuccessReleaseDialog.tsx +6 -9
  13. package/templates/escrows/details/useDetailsEscrow.ts +2 -2
  14. package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +111 -60
  15. package/templates/escrows/escrows-by-role/cards/Filters.tsx +3 -5
  16. package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +36 -38
  17. package/templates/escrows/escrows-by-role/table/Filters.tsx +3 -5
  18. package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +33 -25
  19. package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +107 -67
  20. package/templates/escrows/escrows-by-signer/cards/Filters.tsx +3 -5
  21. package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +28 -38
  22. package/templates/escrows/escrows-by-signer/table/Filters.tsx +3 -5
  23. package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +32 -25
  24. package/templates/escrows/multi-release/dispute-milestone/button/DisputeEscrow.tsx +98 -0
  25. package/templates/escrows/multi-release/initialize-escrow/dialog/InitializeEscrow.tsx +528 -0
  26. package/templates/escrows/multi-release/initialize-escrow/form/InitializeEscrow.tsx +506 -0
  27. package/templates/escrows/multi-release/initialize-escrow/shared/schema.ts +179 -0
  28. package/templates/escrows/multi-release/initialize-escrow/shared/useInitializeEscrow.ts +175 -0
  29. package/templates/escrows/multi-release/release-milestone/button/ReleaseEscrow.tsx +116 -0
  30. package/templates/escrows/multi-release/resolve-dispute/button/ResolveDispute.tsx +122 -0
  31. package/templates/escrows/multi-release/resolve-dispute/dialog/ResolveDispute.tsx +178 -0
  32. package/templates/escrows/multi-release/resolve-dispute/form/ResolveDispute.tsx +156 -0
  33. package/templates/escrows/multi-release/resolve-dispute/shared/schema.ts +85 -0
  34. package/templates/escrows/multi-release/resolve-dispute/shared/useResolveDispute.ts +105 -0
  35. package/templates/escrows/multi-release/update-escrow/dialog/UpdateEscrow.tsx +471 -0
  36. package/templates/escrows/multi-release/update-escrow/form/UpdateEscrow.tsx +449 -0
  37. package/templates/escrows/multi-release/update-escrow/shared/schema.ts +152 -0
  38. package/templates/escrows/multi-release/update-escrow/shared/useUpdateEscrow.ts +254 -0
  39. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/button/ApproveMilestone.tsx +21 -8
  40. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/dialog/ApproveMilestone.tsx +4 -4
  41. package/templates/escrows/{single-release → single-multi-release}/approve-milestone/form/ApproveMilestone.tsx +4 -4
  42. package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/useApproveMilestone.ts +17 -17
  43. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/button/ChangeMilestoneStatus.tsx +5 -5
  44. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +5 -5
  45. package/templates/escrows/{single-release → single-multi-release}/change-milestone-status/form/ChangeMilestoneStatus.tsx +4 -4
  46. package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/useChangeMilestoneStatus.ts +2 -2
  47. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/button/FundEscrow.tsx +4 -4
  48. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/dialog/FundEscrow.tsx +3 -3
  49. package/templates/escrows/{single-release → single-multi-release}/fund-escrow/form/FundEscrow.tsx +3 -3
  50. package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/useFundEscrow.ts +2 -2
  51. package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +3 -3
  52. package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +14 -6
  53. package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +14 -6
  54. package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +0 -57
  55. package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +43 -2
  56. package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +5 -5
  57. package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +4 -4
  58. package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +4 -7
  59. package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +2 -2
  60. package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +15 -2
  61. package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +2 -2
  62. package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +2 -2
  63. package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +229 -224
  64. package/templates/{escrows/escrow-context → providers}/EscrowDialogsProvider.tsx +1 -3
  65. package/templates/{escrows/escrow-context → providers}/EscrowProvider.tsx +27 -4
  66. package/templates/providers/ReactQueryClientProvider.tsx +3 -1
  67. package/templates/providers/TrustlessWork.tsx +1 -1
  68. package/templates/escrows/details/ProgressEscrow.tsx +0 -191
  69. /package/templates/escrows/{single-release/approve-milestone/shared → single-multi-release/approve-milestone}/schema.ts +0 -0
  70. /package/templates/escrows/{single-release/change-milestone-status/shared → single-multi-release/change-milestone-status}/schema.ts +0 -0
  71. /package/templates/escrows/{single-release/fund-escrow/shared → single-multi-release/fund-escrow}/schema.ts +0 -0
  72. /package/templates/{escrows/escrow-context → providers}/EscrowAmountProvider.tsx +0 -0
@@ -23,14 +23,14 @@ import {
23
23
  TableRow,
24
24
  } from "__UI_BASE__/table";
25
25
  import { FileX, Loader2, Wallet, RefreshCw, AlertTriangle } from "lucide-react";
26
- import Filters from "./Filters";
27
- import EscrowDetailDialog from "../details/EscrowDetailDialog";
28
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
29
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
26
+ import { Filters } from "./Filters";
27
+ import { EscrowDetailDialog } from "../details/EscrowDetailDialog";
28
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
29
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
30
30
  import { useEscrowsByRole } from "../useEscrowsByRole.shared";
31
31
  import { formatTimestamp } from "../../../helpers/format.helper";
32
32
 
33
- export function EscrowsByRoleTable() {
33
+ export const EscrowsByRoleTable = () => {
34
34
  const {
35
35
  walletAddress,
36
36
  data,
@@ -75,6 +75,17 @@ export function EscrowsByRoleTable() {
75
75
  const dialogStates = useEscrowDialogs();
76
76
  const { setSelectedEscrow } = useEscrowContext();
77
77
 
78
+ const handleRefresh = React.useCallback(() => {
79
+ void refetch();
80
+ }, [refetch]);
81
+
82
+ const setRoleStable = React.useCallback(
83
+ (v: Role | undefined) => {
84
+ if (v) setRole(v);
85
+ },
86
+ [setRole]
87
+ );
88
+
78
89
  const columns = React.useMemo<ColumnDef<Escrow>[]>(
79
90
  () => [
80
91
  {
@@ -187,7 +198,7 @@ export function EscrowsByRoleTable() {
187
198
  ),
188
199
  },
189
200
  ],
190
- []
201
+ [dialogStates.second.setIsOpen, setSelectedEscrow]
191
202
  );
192
203
 
193
204
  const table = useReactTable({
@@ -202,18 +213,6 @@ export function EscrowsByRoleTable() {
202
213
  enableSortingRemoval: true,
203
214
  });
204
215
 
205
- /**
206
- * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
207
- *
208
- * You must pass one or more roles according to requirements. Acctually it's coming from the select filter.
209
- *
210
- * For example:
211
- * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
212
- *
213
- * Depending of the role, you'll have different actions buttons
214
- */
215
- const activeRole: Role[] = role.split(",") as Role[];
216
-
217
216
  const escrows = data ?? [];
218
217
 
219
218
  return (
@@ -235,19 +234,19 @@ export function EscrowsByRoleTable() {
235
234
  setEngagementId={setEngagementId}
236
235
  setIsActive={setIsActive}
237
236
  setValidateOnChain={setValidateOnChain}
238
- setType={(v) => setType(v as typeof type)}
239
- setStatus={(v) => setStatus(v as typeof status)}
237
+ setType={setType}
238
+ setStatus={setStatus}
240
239
  setMinAmount={setMinAmount}
241
240
  setMaxAmount={setMaxAmount}
242
241
  setDateRange={setDateRange}
243
- setRole={(v) => setRole(v as Role)}
242
+ setRole={setRoleStable}
244
243
  onClearFilters={onClearFilters}
245
- onRefresh={() => refetch()}
244
+ onRefresh={handleRefresh}
246
245
  isRefreshing={isFetching}
247
246
  orderBy={orderBy}
248
247
  orderDirection={orderDirection}
249
- setOrderBy={(v) => setOrderBy(v)}
250
- setOrderDirection={(v) => setOrderDirection(v)}
248
+ setOrderBy={setOrderBy}
249
+ setOrderDirection={setOrderDirection}
251
250
  />
252
251
 
253
252
  <Card className="w-full py-2 sm:py-4">
@@ -262,8 +261,8 @@ export function EscrowsByRoleTable() {
262
261
  const className =
263
262
  typeof header.column.columnDef.meta === "object" &&
264
263
  header.column.columnDef.meta &&
265
- "className" in (header.column.columnDef.meta as any)
266
- ? (header.column.columnDef.meta as any).className
264
+ "className" in header.column.columnDef.meta
265
+ ? header.column.columnDef.meta.className
267
266
  : "";
268
267
  return (
269
268
  <TableHead
@@ -346,7 +345,7 @@ export function EscrowsByRoleTable() {
346
345
  <Button
347
346
  variant="outline"
348
347
  size="sm"
349
- onClick={() => refetch()}
348
+ onClick={handleRefresh}
350
349
  >
351
350
  <RefreshCw className="h-4 w-4 mr-2" />
352
351
  Retry
@@ -376,8 +375,8 @@ export function EscrowsByRoleTable() {
376
375
  const className =
377
376
  typeof cell.column.columnDef.meta === "object" &&
378
377
  cell.column.columnDef.meta &&
379
- "className" in (cell.column.columnDef.meta as any)
380
- ? (cell.column.columnDef.meta as any).className
378
+ "className" in cell.column.columnDef.meta
379
+ ? (cell.column.columnDef.meta.className as string)
381
380
  : "";
382
381
  return (
383
382
  <TableCell key={cell.id} className={className}>
@@ -425,14 +424,13 @@ export function EscrowsByRoleTable() {
425
424
  </div>
426
425
 
427
426
  {/* Dialog */}
428
- <EscrowDetailDialog
429
- activeRole={activeRole}
430
- isDialogOpen={dialogStates.second.isOpen}
431
- setIsDialogOpen={dialogStates.second.setIsOpen}
432
- setSelectedEscrow={setSelectedEscrow}
433
- />
427
+ {dialogStates.second.isOpen ? (
428
+ <EscrowDetailDialog
429
+ isDialogOpen={dialogStates.second.isOpen}
430
+ setIsDialogOpen={dialogStates.second.setIsOpen}
431
+ setSelectedEscrow={setSelectedEscrow}
432
+ />
433
+ ) : null}
434
434
  </>
435
435
  );
436
- }
437
-
438
- export default EscrowsByRoleTable;
436
+ };
@@ -84,7 +84,7 @@ type FiltersProps = {
84
84
  setOrderDirection: (v: "asc" | "desc") => void;
85
85
  };
86
86
 
87
- function Filters({
87
+ export const Filters = ({
88
88
  title,
89
89
  engagementId,
90
90
  isActive,
@@ -113,7 +113,7 @@ function Filters({
113
113
  onRefresh,
114
114
  setOrderBy,
115
115
  setOrderDirection,
116
- }: FiltersProps) {
116
+ }: FiltersProps) => {
117
117
  return (
118
118
  <div className="w-full bg-card/50 backdrop-blur-sm border border-border/50 rounded-lg p-4 shadow-sm">
119
119
  {/* Header Section */}
@@ -416,6 +416,4 @@ function Filters({
416
416
  </div>
417
417
  </div>
418
418
  );
419
- }
420
-
421
- export default React.memo(Filters);
419
+ };
@@ -108,8 +108,8 @@ export function useEscrowsByRole() {
108
108
  // eslint-disable-next-line react-hooks/exhaustive-deps
109
109
  }, []);
110
110
 
111
- const debouncedSearchParams = useDebouncedValue(
112
- {
111
+ const stableSearchParams = React.useMemo(
112
+ () => ({
113
113
  page,
114
114
  orderBy,
115
115
  orderDirection,
@@ -126,10 +126,29 @@ export function useEscrowsByRole() {
126
126
  : undefined,
127
127
  endDate: dateRange.to ? endOfDay(dateRange.to).toISOString() : undefined,
128
128
  role,
129
- },
130
- 200
129
+ }),
130
+ [
131
+ page,
132
+ orderBy,
133
+ orderDirection,
134
+ debouncedTitle,
135
+ debouncedEngagementId,
136
+ isActive,
137
+ validateOnChain,
138
+ type,
139
+ status,
140
+ debouncedMinAmount,
141
+ debouncedMaxAmount,
142
+ dateRange.from,
143
+ dateRange.to,
144
+ role,
145
+ ]
131
146
  );
132
147
 
148
+ const debouncedSearchParams = useDebouncedValue(stableSearchParams, 200);
149
+
150
+ const lastQueryStringRef = React.useRef("");
151
+
133
152
  React.useEffect(() => {
134
153
  if (!pathname) return;
135
154
  const qp = new URLSearchParams();
@@ -145,8 +164,10 @@ export function useEscrowsByRole() {
145
164
  qp.set("engagementId", debouncedSearchParams.engagementId);
146
165
  qp.set("isActive", String(debouncedSearchParams.isActive));
147
166
  qp.set("validateOnChain", String(debouncedSearchParams.validateOnChain));
148
- if (type && type !== "all") qp.set("type", type);
149
- if (status && status !== "all") qp.set("status", status);
167
+ if (debouncedSearchParams.type && debouncedSearchParams.type !== "all")
168
+ qp.set("type", debouncedSearchParams.type);
169
+ if (debouncedSearchParams.status && debouncedSearchParams.status !== "all")
170
+ qp.set("status", debouncedSearchParams.status);
150
171
  if (debouncedSearchParams.minAmount)
151
172
  qp.set("minAmount", String(debouncedSearchParams.minAmount));
152
173
  if (debouncedSearchParams.maxAmount)
@@ -158,25 +179,12 @@ export function useEscrowsByRole() {
158
179
  if (debouncedSearchParams.role)
159
180
  qp.set("role", String(debouncedSearchParams.role));
160
181
 
161
- router.replace(`${pathname}?${qp.toString()}`);
162
- }, [
163
- pathname,
164
- router,
165
- debouncedSearchParams.page,
166
- debouncedSearchParams.orderBy,
167
- debouncedSearchParams.orderDirection,
168
- debouncedSearchParams.title,
169
- debouncedSearchParams.engagementId,
170
- debouncedSearchParams.isActive,
171
- debouncedSearchParams.validateOnChain,
172
- type,
173
- status,
174
- debouncedSearchParams.minAmount,
175
- debouncedSearchParams.maxAmount,
176
- debouncedSearchParams.startDate,
177
- debouncedSearchParams.endDate,
178
- debouncedSearchParams.role,
179
- ]);
182
+ const newQs = qp.toString();
183
+ if (lastQueryStringRef.current !== newQs) {
184
+ lastQueryStringRef.current = newQs;
185
+ router.replace(`${pathname}?${newQs}`);
186
+ }
187
+ }, [pathname, router, debouncedSearchParams]);
180
188
 
181
189
  const formattedRangeLabel = React.useMemo(() => {
182
190
  if (!dateRange?.from && !dateRange?.to) return "Date range";
@@ -5,14 +5,14 @@ import { Button } from "__UI_BASE__/button";
5
5
  import type {
6
6
  GetEscrowsFromIndexerResponse as Escrow,
7
7
  MultiReleaseMilestone,
8
- Role,
9
8
  SingleReleaseMilestone,
10
9
  } from "@trustless-work/escrow/types";
11
- import Filters from "./Filters";
10
+ import { Filters } from "./Filters";
12
11
  import { useEscrowsBySigner } from "../useEscrowsBySigner.shared";
13
12
  import { Card, CardContent, CardHeader, CardTitle } from "__UI_BASE__/card";
14
13
  import { Badge } from "__UI_BASE__/badge";
15
14
  import { Separator } from "__UI_BASE__/separator";
15
+ import { Tooltip, TooltipContent, TooltipTrigger } from "__UI_BASE__/tooltip";
16
16
  import {
17
17
  Goal,
18
18
  Wallet,
@@ -21,12 +21,15 @@ import {
21
21
  RefreshCw,
22
22
  FileX,
23
23
  } from "lucide-react";
24
- import { useEscrowContext } from "../../escrow-context/EscrowProvider";
25
- import { useEscrowDialogs } from "../../escrow-context/EscrowDialogsProvider";
26
- import EscrowDetailDialog from "../../escrows-by-role/details/EscrowDetailDialog";
27
- import { formatTimestamp } from "../../../helpers/format.helper";
24
+ import { useEscrowContext } from "@/components/tw-blocks/providers/EscrowProvider";
25
+ import { useEscrowDialogs } from "@/components/tw-blocks/providers/EscrowDialogsProvider";
26
+ import { EscrowDetailDialog } from "../details/EscrowDetailDialog";
27
+ import {
28
+ formatCurrency,
29
+ formatTimestamp,
30
+ } from "../../../helpers/format.helper";
28
31
 
29
- export function EscrowsBySignerCards() {
32
+ export const EscrowsBySignerCards = () => {
30
33
  const {
31
34
  walletAddress,
32
35
  data,
@@ -70,9 +73,9 @@ export function EscrowsBySignerCards() {
70
73
 
71
74
  const dialogStates = useEscrowDialogs();
72
75
 
73
- const formatCurrency = (value: number, currency: string) => {
74
- return `${currency} ${value.toFixed(2)}`;
75
- };
76
+ const handleRefresh = React.useCallback(() => {
77
+ void refetch();
78
+ }, [refetch]);
76
79
 
77
80
  function allMilestonesReleasedOrResolved(
78
81
  milestones: MultiReleaseMilestone[]
@@ -116,18 +119,6 @@ export function EscrowsBySignerCards() {
116
119
  dialogStates.second.setIsOpen(true);
117
120
  };
118
121
 
119
- /**
120
- * Based on the provided roles -> https://docs.trustlesswork.com/trustless-work/technology-overview/roles-in-trustless-work
121
- *
122
- * You must pass one or more roles according to requirements
123
- *
124
- * For example:
125
- * - If the user is a freelancer, you must pass the "serviceProvider" and "receiver" role
126
- *
127
- * Depending of the role, you'll have different actions buttons
128
- */
129
- const activeRole: Role[] = ["approver"];
130
-
131
122
  const escrows: Escrow[] = data ?? [];
132
123
 
133
124
  return (
@@ -148,18 +139,18 @@ export function EscrowsBySignerCards() {
148
139
  setEngagementId={setEngagementId}
149
140
  setIsActive={setIsActive}
150
141
  setValidateOnChain={setValidateOnChain}
151
- setType={(v) => setType(v as typeof type)}
152
- setStatus={(v) => setStatus(v as typeof status)}
142
+ setType={setType}
143
+ setStatus={setStatus}
153
144
  setMinAmount={setMinAmount}
154
145
  setMaxAmount={setMaxAmount}
155
146
  setDateRange={setDateRange}
156
147
  onClearFilters={onClearFilters}
157
- onRefresh={() => refetch()}
148
+ onRefresh={handleRefresh}
158
149
  isRefreshing={isFetching}
159
150
  orderBy={orderBy}
160
151
  orderDirection={orderDirection}
161
- setOrderBy={(v) => setOrderBy(v)}
162
- setOrderDirection={(v) => setOrderDirection(v)}
152
+ setOrderBy={setOrderBy}
153
+ setOrderDirection={setOrderDirection}
163
154
  />
164
155
 
165
156
  <div className="w-full py-2 sm:py-4">
@@ -235,7 +226,7 @@ export function EscrowsBySignerCards() {
235
226
  An error occurred while loading the information. Please try
236
227
  again.
237
228
  </p>
238
- <Button variant="outline" size="sm" onClick={() => refetch()}>
229
+ <Button variant="outline" size="sm" onClick={handleRefresh}>
239
230
  <RefreshCw className="h-4 w-4 mr-2" />
240
231
  Retry
241
232
  </Button>
@@ -258,7 +249,7 @@ export function EscrowsBySignerCards() {
258
249
  {escrows.map((escrow) => (
259
250
  <React.Fragment key={escrow.contractId}>
260
251
  <Card
261
- className="w-full max-w-md mx-auto hover:shadow-lg transition-shadow duration-200"
252
+ className="w-full max-w-md mx-auto hover:shadow-lg transition-shadow duration-200 cursor-pointer"
262
253
  onClick={(e) => {
263
254
  e.stopPropagation();
264
255
  onCardClick(escrow);
@@ -343,9 +334,9 @@ export function EscrowsBySignerCards() {
343
334
  <ul className="list-disc list-inside flex flex-col gap-1">
344
335
  {escrow.milestones
345
336
  .slice(0, 3)
346
- .map((milestone) => (
337
+ .map((milestone, index) => (
347
338
  <li
348
- key={milestone.description.slice(0, 5)}
339
+ key={`milestone-${milestone.description}-${milestone.status}-${index}`}
349
340
  className="text-xs flex justify-between"
350
341
  >
351
342
  {milestone.description}
@@ -361,33 +352,83 @@ export function EscrowsBySignerCards() {
361
352
  )}
362
353
  </span>
363
354
 
364
- <span
365
- className={`bg-red-800 rounded-full h-2 w-2 ml-1 ${
366
- milestone.flags?.disputed
367
- ? "block"
368
- : "hidden"
369
- }`}
370
- />
371
-
372
- <span
373
- className={`bg-green-800 rounded-full h-2 w-2 ml-1 ${
374
- milestone.flags?.resolved ||
375
- milestone.flags?.released
376
- ? "block"
377
- : "hidden"
378
- }`}
379
- />
380
-
381
- <span
382
- className={`bg-yellow-800 rounded-full h-2 w-2 ml-1 ${
383
- milestone.flags?.approved &&
384
- !milestone.flags?.disputed &&
385
- !milestone.flags?.resolved &&
386
- !milestone.flags?.released
387
- ? "block"
388
- : "hidden"
389
- }`}
390
- />
355
+ {milestone.flags?.disputed && (
356
+ <Tooltip>
357
+ <TooltipTrigger>
358
+ <span
359
+ className={`bg-red-800 rounded-full h-2 w-2 ml-1 ${
360
+ milestone.flags?.disputed
361
+ ? "block"
362
+ : "hidden"
363
+ }`}
364
+ />
365
+ </TooltipTrigger>
366
+ <TooltipContent>
367
+ Disputed
368
+ </TooltipContent>
369
+ </Tooltip>
370
+ )}
371
+
372
+ {milestone.flags?.resolved && (
373
+ <Tooltip>
374
+ <TooltipTrigger>
375
+ <span
376
+ className={`bg-green-800 rounded-full h-2 w-2 ml-1 ${
377
+ milestone.flags?.resolved
378
+ ? "block"
379
+ : "hidden"
380
+ }`}
381
+ />
382
+ </TooltipTrigger>
383
+ <TooltipContent>
384
+ Resolved
385
+ </TooltipContent>
386
+ </Tooltip>
387
+ )}
388
+
389
+ {milestone.flags?.released && (
390
+ <Tooltip>
391
+ <TooltipTrigger>
392
+ <span
393
+ className={`bg-green-800 rounded-full h-2 w-2 ml-1 ${
394
+ milestone.flags?.released
395
+ ? "block"
396
+ : "hidden"
397
+ }`}
398
+ />
399
+ </TooltipTrigger>
400
+ <TooltipContent>
401
+ Released
402
+ </TooltipContent>
403
+ </Tooltip>
404
+ )}
405
+
406
+ {milestone.flags?.approved &&
407
+ !milestone.flags?.disputed &&
408
+ !milestone.flags?.resolved &&
409
+ !milestone.flags?.released && (
410
+ <Tooltip>
411
+ <TooltipTrigger>
412
+ <span
413
+ className={`bg-yellow-600 rounded-full h-2 w-2 ml-1 ${
414
+ milestone.flags
415
+ ?.approved &&
416
+ !milestone.flags
417
+ ?.disputed &&
418
+ !milestone.flags
419
+ ?.resolved &&
420
+ !milestone.flags
421
+ ?.released
422
+ ? "block"
423
+ : "hidden"
424
+ }`}
425
+ />
426
+ </TooltipTrigger>
427
+ <TooltipContent>
428
+ Pending Release
429
+ </TooltipContent>
430
+ </Tooltip>
431
+ )}
391
432
  </div>
392
433
  </>
393
434
  )}
@@ -502,14 +543,13 @@ export function EscrowsBySignerCards() {
502
543
  </div>
503
544
 
504
545
  {/* Dialog */}
505
- <EscrowDetailDialog
506
- activeRole={activeRole}
507
- isDialogOpen={dialogStates.second.isOpen}
508
- setIsDialogOpen={dialogStates.second.setIsOpen}
509
- setSelectedEscrow={setSelectedEscrow}
510
- />
546
+ {dialogStates.second.isOpen ? (
547
+ <EscrowDetailDialog
548
+ isDialogOpen={dialogStates.second.isOpen}
549
+ setIsDialogOpen={dialogStates.second.setIsOpen}
550
+ setSelectedEscrow={setSelectedEscrow}
551
+ />
552
+ ) : null}
511
553
  </>
512
554
  );
513
- }
514
-
515
- export default EscrowsBySignerCards;
555
+ };
@@ -81,7 +81,7 @@ type FiltersProps = {
81
81
  setOrderDirection: (v: "asc" | "desc") => void;
82
82
  };
83
83
 
84
- function Filters({
84
+ export const Filters = ({
85
85
  title,
86
86
  engagementId,
87
87
  isActive,
@@ -108,7 +108,7 @@ function Filters({
108
108
  onRefresh,
109
109
  setOrderBy,
110
110
  setOrderDirection,
111
- }: FiltersProps) {
111
+ }: FiltersProps) => {
112
112
  return (
113
113
  <div className="w-full bg-card/50 backdrop-blur-sm border border-border/50 rounded-lg p-4 shadow-sm">
114
114
  {/* Header Section */}
@@ -384,6 +384,4 @@ function Filters({
384
384
  </div>
385
385
  </div>
386
386
  );
387
- }
388
-
389
- export default React.memo(Filters);
387
+ };