@trustless-work/blocks 0.0.1
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.
- package/README.md +96 -0
- package/bin/index.js +1123 -0
- package/package.json +44 -0
- package/templates/deps.json +29 -0
- package/templates/escrows/details/Actions.tsx +149 -0
- package/templates/escrows/details/Entities.tsx +48 -0
- package/templates/escrows/details/EntityCard.tsx +98 -0
- package/templates/escrows/details/EscrowDetailDialog.tsx +154 -0
- package/templates/escrows/details/GeneralInformation.tsx +329 -0
- package/templates/escrows/details/MilestoneCard.tsx +254 -0
- package/templates/escrows/details/MilestoneDetailDialog.tsx +276 -0
- package/templates/escrows/details/Milestones.tsx +87 -0
- package/templates/escrows/details/ProgressEscrow.tsx +191 -0
- package/templates/escrows/details/StatisticsCard.tsx +79 -0
- package/templates/escrows/details/SuccessReleaseDialog.tsx +101 -0
- package/templates/escrows/details/useDetailsEscrow.ts +126 -0
- package/templates/escrows/escrow-context/EscrowAmountProvider.tsx +86 -0
- package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +108 -0
- package/templates/escrows/escrow-context/EscrowProvider.tsx +124 -0
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +503 -0
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +427 -0
- package/templates/escrows/escrows-by-role/table/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +336 -0
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +502 -0
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +422 -0
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +320 -0
- package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +78 -0
- package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +102 -0
- package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +80 -0
- package/templates/escrows/single-release/approve-milestone/shared/schema.ts +9 -0
- package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +67 -0
- package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +78 -0
- package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +167 -0
- package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +114 -0
- package/templates/escrows/single-release/change-milestone-status/shared/schema.ts +15 -0
- package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +77 -0
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +68 -0
- package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +84 -0
- package/templates/escrows/single-release/fund-escrow/dialog/FundEscrow.tsx +77 -0
- package/templates/escrows/single-release/fund-escrow/form/FundEscrow.tsx +54 -0
- package/templates/escrows/single-release/fund-escrow/shared/schema.ts +10 -0
- package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +66 -0
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +526 -0
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +504 -0
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +232 -0
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +115 -0
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +80 -0
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +94 -0
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +123 -0
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +58 -0
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +485 -0
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +463 -0
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +139 -0
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +211 -0
- package/templates/handle-errors/errors.enum.ts +6 -0
- package/templates/handle-errors/handle.ts +47 -0
- package/templates/helpers/format.helper.ts +27 -0
- package/templates/helpers/useCopy.ts +13 -0
- package/templates/providers/ReactQueryClientProvider.tsx +28 -0
- package/templates/providers/TrustlessWork.tsx +30 -0
- package/templates/tanstak/useEscrowsByRoleQuery.ts +87 -0
- package/templates/tanstak/useEscrowsBySignerQuery.ts +78 -0
- package/templates/tanstak/useEscrowsMutations.ts +411 -0
- package/templates/wallet-kit/WalletButtons.tsx +116 -0
- package/templates/wallet-kit/WalletProvider.tsx +94 -0
- package/templates/wallet-kit/trustlines.ts +40 -0
- package/templates/wallet-kit/useWallet.ts +77 -0
- package/templates/wallet-kit/validators.ts +12 -0
- package/templates/wallet-kit/wallet-kit.ts +30 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Button } from "__UI_BASE__/button";
|
|
5
|
+
import type {
|
|
6
|
+
GetEscrowsFromIndexerResponse as Escrow,
|
|
7
|
+
MultiReleaseMilestone,
|
|
8
|
+
Role,
|
|
9
|
+
SingleReleaseMilestone,
|
|
10
|
+
} from "@trustless-work/escrow/types";
|
|
11
|
+
import Filters from "./Filters";
|
|
12
|
+
import { useEscrowsBySigner } from "../useEscrowsBySigner.shared";
|
|
13
|
+
import { Card, CardContent, CardHeader, CardTitle } from "__UI_BASE__/card";
|
|
14
|
+
import { Badge } from "__UI_BASE__/badge";
|
|
15
|
+
import { Separator } from "__UI_BASE__/separator";
|
|
16
|
+
import {
|
|
17
|
+
Goal,
|
|
18
|
+
Wallet,
|
|
19
|
+
Loader2,
|
|
20
|
+
AlertTriangle,
|
|
21
|
+
RefreshCw,
|
|
22
|
+
FileX,
|
|
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";
|
|
28
|
+
|
|
29
|
+
export function EscrowsBySignerCards() {
|
|
30
|
+
const {
|
|
31
|
+
walletAddress,
|
|
32
|
+
data,
|
|
33
|
+
isLoading,
|
|
34
|
+
isError,
|
|
35
|
+
refetch,
|
|
36
|
+
isFetching,
|
|
37
|
+
nextData,
|
|
38
|
+
isFetchingNext,
|
|
39
|
+
page,
|
|
40
|
+
setPage,
|
|
41
|
+
orderBy,
|
|
42
|
+
setOrderBy,
|
|
43
|
+
orderDirection,
|
|
44
|
+
setOrderDirection,
|
|
45
|
+
sorting,
|
|
46
|
+
title,
|
|
47
|
+
setTitle,
|
|
48
|
+
engagementId,
|
|
49
|
+
setEngagementId,
|
|
50
|
+
isActive,
|
|
51
|
+
setIsActive,
|
|
52
|
+
validateOnChain,
|
|
53
|
+
setValidateOnChain,
|
|
54
|
+
type,
|
|
55
|
+
setType,
|
|
56
|
+
status,
|
|
57
|
+
setStatus,
|
|
58
|
+
minAmount,
|
|
59
|
+
setMinAmount,
|
|
60
|
+
maxAmount,
|
|
61
|
+
setMaxAmount,
|
|
62
|
+
dateRange,
|
|
63
|
+
setDateRange,
|
|
64
|
+
formattedRangeLabel,
|
|
65
|
+
onClearFilters,
|
|
66
|
+
handleSortingChange,
|
|
67
|
+
} = useEscrowsBySigner();
|
|
68
|
+
|
|
69
|
+
const { setSelectedEscrow } = useEscrowContext();
|
|
70
|
+
|
|
71
|
+
const dialogStates = useEscrowDialogs();
|
|
72
|
+
|
|
73
|
+
const formatCurrency = (value: number, currency: string) => {
|
|
74
|
+
return `${currency} ${value.toFixed(2)}`;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function allMilestonesReleasedOrResolved(
|
|
78
|
+
milestones: MultiReleaseMilestone[]
|
|
79
|
+
) {
|
|
80
|
+
return milestones.every(
|
|
81
|
+
(milestone) => milestone.flags?.released || milestone.flags?.resolved
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function allMilestonesApproved(milestones: SingleReleaseMilestone[]) {
|
|
86
|
+
return milestones.every((milestone) => milestone.approved);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getSingleReleaseStatus(
|
|
90
|
+
flags: { disputed?: boolean; resolved?: boolean; released?: boolean } = {}
|
|
91
|
+
) {
|
|
92
|
+
if (flags.disputed) return { label: "Disputed", variant: "destructive" };
|
|
93
|
+
if (flags.resolved) return { label: "Resolved", variant: "outline" };
|
|
94
|
+
if (flags.released) return { label: "Released", variant: "outline" };
|
|
95
|
+
return { label: "Working", variant: "outline" };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const currentSort = sorting?.[0];
|
|
99
|
+
const sortField =
|
|
100
|
+
(currentSort?.id as "amount" | "createdAt" | "updatedAt" | undefined) ??
|
|
101
|
+
undefined;
|
|
102
|
+
const sortDesc = currentSort?.desc ?? true;
|
|
103
|
+
|
|
104
|
+
const setSort = (field: "amount" | "createdAt" | "updatedAt") => {
|
|
105
|
+
if (sortField === field) {
|
|
106
|
+
handleSortingChange([{ id: field, desc: !sortDesc }]);
|
|
107
|
+
} else {
|
|
108
|
+
handleSortingChange([{ id: field, desc: true }]);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const clearSort = () => handleSortingChange([]);
|
|
113
|
+
|
|
114
|
+
const onCardClick = (escrow: Escrow) => {
|
|
115
|
+
setSelectedEscrow(escrow);
|
|
116
|
+
dialogStates.second.setIsOpen(true);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const activeRole: Role[] = ["approver"];
|
|
120
|
+
const escrows: Escrow[] = data ?? [];
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<>
|
|
124
|
+
<div className="w-full flex flex-col gap-4">
|
|
125
|
+
<Filters
|
|
126
|
+
title={title}
|
|
127
|
+
engagementId={engagementId}
|
|
128
|
+
isActive={isActive}
|
|
129
|
+
validateOnChain={validateOnChain}
|
|
130
|
+
type={type}
|
|
131
|
+
status={status}
|
|
132
|
+
minAmount={minAmount}
|
|
133
|
+
maxAmount={maxAmount}
|
|
134
|
+
dateRange={dateRange}
|
|
135
|
+
formattedRangeLabel={formattedRangeLabel}
|
|
136
|
+
setTitle={setTitle}
|
|
137
|
+
setEngagementId={setEngagementId}
|
|
138
|
+
setIsActive={setIsActive}
|
|
139
|
+
setValidateOnChain={setValidateOnChain}
|
|
140
|
+
setType={(v) => setType(v as typeof type)}
|
|
141
|
+
setStatus={(v) => setStatus(v as typeof status)}
|
|
142
|
+
setMinAmount={setMinAmount}
|
|
143
|
+
setMaxAmount={setMaxAmount}
|
|
144
|
+
setDateRange={setDateRange}
|
|
145
|
+
onClearFilters={onClearFilters}
|
|
146
|
+
onRefresh={() => refetch()}
|
|
147
|
+
isRefreshing={isFetching}
|
|
148
|
+
orderBy={orderBy}
|
|
149
|
+
orderDirection={orderDirection}
|
|
150
|
+
setOrderBy={(v) => setOrderBy(v)}
|
|
151
|
+
setOrderDirection={(v) => setOrderDirection(v)}
|
|
152
|
+
/>
|
|
153
|
+
|
|
154
|
+
<div className="w-full p-2 sm:p-4">
|
|
155
|
+
<div className="mb-2 sm:mb-3 flex items-center justify-end gap-2">
|
|
156
|
+
<span className="text-xs text-muted-foreground">Sort</span>
|
|
157
|
+
<Button
|
|
158
|
+
className="cursor-pointer"
|
|
159
|
+
variant={sortField === "createdAt" ? "default" : "outline"}
|
|
160
|
+
size="sm"
|
|
161
|
+
onClick={() => setSort("createdAt")}
|
|
162
|
+
>
|
|
163
|
+
Created {sortField === "createdAt" ? (sortDesc ? "▼" : "▲") : ""}
|
|
164
|
+
</Button>
|
|
165
|
+
<Button
|
|
166
|
+
className="cursor-pointer"
|
|
167
|
+
variant={sortField === "updatedAt" ? "default" : "outline"}
|
|
168
|
+
size="sm"
|
|
169
|
+
onClick={() => setSort("updatedAt")}
|
|
170
|
+
>
|
|
171
|
+
Updated {sortField === "updatedAt" ? (sortDesc ? "▼" : "▲") : ""}
|
|
172
|
+
</Button>
|
|
173
|
+
<Button
|
|
174
|
+
className="cursor-pointer"
|
|
175
|
+
variant={sortField === "amount" ? "default" : "outline"}
|
|
176
|
+
size="sm"
|
|
177
|
+
onClick={() => setSort("amount")}
|
|
178
|
+
>
|
|
179
|
+
Amount {sortField === "amount" ? (sortDesc ? "▼" : "▲") : ""}
|
|
180
|
+
</Button>
|
|
181
|
+
<Button
|
|
182
|
+
className="cursor-pointer"
|
|
183
|
+
variant="ghost"
|
|
184
|
+
size="sm"
|
|
185
|
+
onClick={clearSort}
|
|
186
|
+
disabled={!currentSort}
|
|
187
|
+
>
|
|
188
|
+
Reset
|
|
189
|
+
</Button>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="mt-2 sm:mt-4 overflow-x-auto">
|
|
192
|
+
{!walletAddress ? (
|
|
193
|
+
<div>
|
|
194
|
+
<div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
|
|
195
|
+
<Wallet className="h-8 w-8 md:h-12 md:w-12 text-primary mb-3" />
|
|
196
|
+
<h3 className="font-medium text-foreground mb-2">
|
|
197
|
+
Connect your wallet
|
|
198
|
+
</h3>
|
|
199
|
+
<p className="text-sm text-muted-foreground max-w-sm">
|
|
200
|
+
To continue, connect your wallet and authorize the
|
|
201
|
+
application.
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
) : isLoading ? (
|
|
206
|
+
<div>
|
|
207
|
+
<div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
|
|
208
|
+
<Loader2 className="h-6 w-6 md:h-8 md:w-8 animate-spin text-primary mb-3" />
|
|
209
|
+
<p className="text-sm text-muted-foreground">
|
|
210
|
+
Loading escrows…
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
) : isError ? (
|
|
215
|
+
<div>
|
|
216
|
+
<div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
|
|
217
|
+
<AlertTriangle className="h-8 w-8 md:h-10 md:w-10 text-destructive mb-3" />
|
|
218
|
+
<h3 className="font-medium text-foreground mb-2">
|
|
219
|
+
Error loading data
|
|
220
|
+
</h3>
|
|
221
|
+
<p className="text-sm text-muted-foreground max-w-sm mb-4">
|
|
222
|
+
An error occurred while loading the information. Please try
|
|
223
|
+
again.
|
|
224
|
+
</p>
|
|
225
|
+
<Button variant="outline" size="sm" onClick={() => refetch()}>
|
|
226
|
+
<RefreshCw className="h-4 w-4 mr-2" />
|
|
227
|
+
Retry
|
|
228
|
+
</Button>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
) : escrows.length === 0 ? (
|
|
232
|
+
<div>
|
|
233
|
+
<div className="p-6 md:p-8 flex flex-col items-center justify-center text-center">
|
|
234
|
+
<FileX className="h-8 w-8 md:h-10 md:w-10 text-muted-foreground/60 mb-3" />
|
|
235
|
+
<h3 className="font-medium text-foreground mb-2">
|
|
236
|
+
No data available
|
|
237
|
+
</h3>
|
|
238
|
+
<p className="text-sm text-muted-foreground">
|
|
239
|
+
No escrows found for the selected filters.
|
|
240
|
+
</p>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
) : (
|
|
244
|
+
<div className="w-full grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 py-2">
|
|
245
|
+
{escrows.map((escrow) => (
|
|
246
|
+
<React.Fragment key={escrow.contractId}>
|
|
247
|
+
<Card
|
|
248
|
+
className="w-full max-w-md mx-auto hover:shadow-lg transition-shadow duration-200"
|
|
249
|
+
onClick={(e) => {
|
|
250
|
+
e.stopPropagation();
|
|
251
|
+
onCardClick(escrow);
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<CardHeader className="pb-3">
|
|
255
|
+
<div className="flex items-start justify-between gap-2">
|
|
256
|
+
<CardTitle className="text-lg font-semibold leading-tight line-clamp-2">
|
|
257
|
+
{escrow.title}
|
|
258
|
+
</CardTitle>
|
|
259
|
+
<Badge
|
|
260
|
+
variant={isActive ? "default" : "destructive"}
|
|
261
|
+
className="shrink-0"
|
|
262
|
+
>
|
|
263
|
+
{isActive ? "Active" : "Inactive"}
|
|
264
|
+
</Badge>
|
|
265
|
+
</div>
|
|
266
|
+
<p className="text-sm text-muted-foreground line-clamp-2 mt-1">
|
|
267
|
+
{escrow.description}
|
|
268
|
+
</p>
|
|
269
|
+
</CardHeader>
|
|
270
|
+
|
|
271
|
+
<CardContent className="space-y-4">
|
|
272
|
+
{/* Amount Section */}
|
|
273
|
+
<div className="space-y-2">
|
|
274
|
+
<div className="flex items-center justify-between">
|
|
275
|
+
<span className="text-sm font-medium">Amount</span>
|
|
276
|
+
<span className="font-semibold">
|
|
277
|
+
{escrow.type === "single-release"
|
|
278
|
+
? formatCurrency(
|
|
279
|
+
escrow.amount,
|
|
280
|
+
escrow.trustline.name
|
|
281
|
+
)
|
|
282
|
+
: formatCurrency(
|
|
283
|
+
escrow.milestones.reduce(
|
|
284
|
+
(acc, milestone) =>
|
|
285
|
+
acc +
|
|
286
|
+
(milestone as MultiReleaseMilestone)
|
|
287
|
+
.amount,
|
|
288
|
+
0
|
|
289
|
+
),
|
|
290
|
+
escrow.trustline.name
|
|
291
|
+
)}
|
|
292
|
+
</span>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
{escrow.balance !== undefined && (
|
|
296
|
+
<div className="flex items-center justify-between text-sm">
|
|
297
|
+
<span className="text-muted-foreground">
|
|
298
|
+
Balance
|
|
299
|
+
</span>
|
|
300
|
+
<span className="font-medium text-green-800 dark:text-green-600">
|
|
301
|
+
{formatCurrency(
|
|
302
|
+
escrow.balance,
|
|
303
|
+
escrow.trustline.name
|
|
304
|
+
)}
|
|
305
|
+
</span>
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
|
|
309
|
+
<div className="flex items-center justify-between text-sm">
|
|
310
|
+
<span className="text-muted-foreground">
|
|
311
|
+
Platform Fee
|
|
312
|
+
</span>
|
|
313
|
+
<span className="text-muted-foreground">
|
|
314
|
+
{escrow.platformFee}%
|
|
315
|
+
</span>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<Separator />
|
|
320
|
+
|
|
321
|
+
{/* Details Section */}
|
|
322
|
+
<div className="space-y-3">
|
|
323
|
+
<div className="flex flex-col gap-2">
|
|
324
|
+
<div className="flex items-center gap-2">
|
|
325
|
+
<Goal className="h-4 w-4 text-muted-foreground" />
|
|
326
|
+
<span className="text-sm font-medium">
|
|
327
|
+
Milestones
|
|
328
|
+
</span>
|
|
329
|
+
</div>
|
|
330
|
+
<ul className="list-disc list-inside flex flex-col gap-1">
|
|
331
|
+
{escrow.milestones
|
|
332
|
+
.slice(0, 3)
|
|
333
|
+
.map((milestone) => (
|
|
334
|
+
<li
|
|
335
|
+
key={milestone.description.slice(0, 5)}
|
|
336
|
+
className="text-xs flex justify-between"
|
|
337
|
+
>
|
|
338
|
+
{milestone.description}
|
|
339
|
+
|
|
340
|
+
{escrow.type === "multi-release" &&
|
|
341
|
+
"amount" in milestone && (
|
|
342
|
+
<>
|
|
343
|
+
<div className="flex items-center gap-1">
|
|
344
|
+
<span className="text-muted-foreground">
|
|
345
|
+
{formatCurrency(
|
|
346
|
+
milestone.amount,
|
|
347
|
+
escrow.trustline.name
|
|
348
|
+
)}
|
|
349
|
+
</span>
|
|
350
|
+
|
|
351
|
+
<span
|
|
352
|
+
className={`bg-red-800 rounded-full h-2 w-2 ml-1 ${
|
|
353
|
+
milestone.flags?.disputed
|
|
354
|
+
? "block"
|
|
355
|
+
: "hidden"
|
|
356
|
+
}`}
|
|
357
|
+
/>
|
|
358
|
+
|
|
359
|
+
<span
|
|
360
|
+
className={`bg-green-800 rounded-full h-2 w-2 ml-1 ${
|
|
361
|
+
milestone.flags?.resolved ||
|
|
362
|
+
milestone.flags?.released
|
|
363
|
+
? "block"
|
|
364
|
+
: "hidden"
|
|
365
|
+
}`}
|
|
366
|
+
/>
|
|
367
|
+
|
|
368
|
+
<span
|
|
369
|
+
className={`bg-yellow-800 rounded-full h-2 w-2 ml-1 ${
|
|
370
|
+
milestone.flags?.approved &&
|
|
371
|
+
!milestone.flags?.disputed &&
|
|
372
|
+
!milestone.flags?.resolved &&
|
|
373
|
+
!milestone.flags?.released
|
|
374
|
+
? "block"
|
|
375
|
+
: "hidden"
|
|
376
|
+
}`}
|
|
377
|
+
/>
|
|
378
|
+
</div>
|
|
379
|
+
</>
|
|
380
|
+
)}
|
|
381
|
+
</li>
|
|
382
|
+
))}
|
|
383
|
+
|
|
384
|
+
{escrow.milestones.length > 3 && (
|
|
385
|
+
<li className="text-xs">
|
|
386
|
+
{escrow.milestones.length - 3} more
|
|
387
|
+
</li>
|
|
388
|
+
)}
|
|
389
|
+
</ul>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
{/* Type Badge */}
|
|
394
|
+
<div className="pt-2 flex items-end justify-between">
|
|
395
|
+
<div className="flex flex-col gap-2">
|
|
396
|
+
<Badge variant="secondary" className="text-xs">
|
|
397
|
+
{escrow.type
|
|
398
|
+
.replace("_", " ")
|
|
399
|
+
.toLowerCase()
|
|
400
|
+
.replace(/\b\w/g, (l) => l.toUpperCase())}
|
|
401
|
+
</Badge>
|
|
402
|
+
|
|
403
|
+
{escrow.type === "single-release" &&
|
|
404
|
+
!allMilestonesApproved(
|
|
405
|
+
escrow.milestones as SingleReleaseMilestone[]
|
|
406
|
+
) && (
|
|
407
|
+
<Badge
|
|
408
|
+
variant={
|
|
409
|
+
getSingleReleaseStatus(escrow.flags ?? {})
|
|
410
|
+
.variant as "destructive" | "outline"
|
|
411
|
+
}
|
|
412
|
+
className="text-xs"
|
|
413
|
+
>
|
|
414
|
+
{
|
|
415
|
+
getSingleReleaseStatus(escrow.flags ?? {})
|
|
416
|
+
.label
|
|
417
|
+
}
|
|
418
|
+
</Badge>
|
|
419
|
+
)}
|
|
420
|
+
|
|
421
|
+
{escrow.type === "single-release" &&
|
|
422
|
+
allMilestonesApproved(
|
|
423
|
+
escrow.milestones as SingleReleaseMilestone[]
|
|
424
|
+
) &&
|
|
425
|
+
!escrow.flags?.released &&
|
|
426
|
+
!escrow.flags?.resolved &&
|
|
427
|
+
!escrow.flags?.disputed && (
|
|
428
|
+
<Badge variant="outline" className="text-xs">
|
|
429
|
+
Pending Release
|
|
430
|
+
</Badge>
|
|
431
|
+
)}
|
|
432
|
+
|
|
433
|
+
{escrow.type === "multi-release" &&
|
|
434
|
+
allMilestonesReleasedOrResolved(
|
|
435
|
+
escrow.milestones as MultiReleaseMilestone[]
|
|
436
|
+
) && (
|
|
437
|
+
<Badge variant="outline" className="text-xs">
|
|
438
|
+
Finished
|
|
439
|
+
</Badge>
|
|
440
|
+
)}
|
|
441
|
+
{escrow.type === "multi-release" &&
|
|
442
|
+
!allMilestonesReleasedOrResolved(
|
|
443
|
+
escrow.milestones as MultiReleaseMilestone[]
|
|
444
|
+
) && (
|
|
445
|
+
<Badge variant="outline" className="text-xs">
|
|
446
|
+
Working
|
|
447
|
+
</Badge>
|
|
448
|
+
)}
|
|
449
|
+
</div>
|
|
450
|
+
|
|
451
|
+
<span className="text-xs text-muted-foreground">
|
|
452
|
+
{formatTimestamp(escrow.createdAt)}
|
|
453
|
+
</span>
|
|
454
|
+
</div>
|
|
455
|
+
</CardContent>
|
|
456
|
+
</Card>
|
|
457
|
+
</React.Fragment>
|
|
458
|
+
))}
|
|
459
|
+
</div>
|
|
460
|
+
)}
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
<div className="mt-3 sm:mt-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
|
464
|
+
<div className="text-xs sm:text-sm text-muted-foreground">
|
|
465
|
+
Page {page}
|
|
466
|
+
</div>
|
|
467
|
+
<div className="flex items-center gap-2 self-end sm:self-auto">
|
|
468
|
+
<Button
|
|
469
|
+
variant="outline"
|
|
470
|
+
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
|
471
|
+
disabled={page === 1 || isFetching}
|
|
472
|
+
>
|
|
473
|
+
Previous
|
|
474
|
+
</Button>
|
|
475
|
+
<Button
|
|
476
|
+
variant="outline"
|
|
477
|
+
onClick={() => setPage((p) => p + 1)}
|
|
478
|
+
disabled={
|
|
479
|
+
isFetching ||
|
|
480
|
+
!walletAddress ||
|
|
481
|
+
((nextData?.length ?? 0) === 0 && !isFetchingNext)
|
|
482
|
+
}
|
|
483
|
+
>
|
|
484
|
+
Next
|
|
485
|
+
</Button>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
{/* Dialog */}
|
|
492
|
+
<EscrowDetailDialog
|
|
493
|
+
activeRole={activeRole}
|
|
494
|
+
isDialogOpen={dialogStates.second.isOpen}
|
|
495
|
+
setIsDialogOpen={dialogStates.second.setIsOpen}
|
|
496
|
+
setSelectedEscrow={setSelectedEscrow}
|
|
497
|
+
/>
|
|
498
|
+
</>
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export default EscrowsBySignerCards;
|