@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,389 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Button } from "__UI_BASE__/button";
|
|
5
|
+
import { Input } from "__UI_BASE__/input";
|
|
6
|
+
import { Checkbox } from "__UI_BASE__/checkbox";
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from "__UI_BASE__/select";
|
|
14
|
+
import { Popover, PopoverContent, PopoverTrigger } from "__UI_BASE__/popover";
|
|
15
|
+
import { Calendar } from "__UI_BASE__/calendar";
|
|
16
|
+
import type { DateRange as DayPickerDateRange } from "react-day-picker";
|
|
17
|
+
import {
|
|
18
|
+
RefreshCcw,
|
|
19
|
+
Trash2,
|
|
20
|
+
Search,
|
|
21
|
+
DollarSign,
|
|
22
|
+
Filter as FilterIcon,
|
|
23
|
+
Calendar as CalendarIcon,
|
|
24
|
+
SlidersHorizontal,
|
|
25
|
+
} from "lucide-react";
|
|
26
|
+
import {
|
|
27
|
+
Select as OrderSelect,
|
|
28
|
+
SelectTrigger as OrderSelectTrigger,
|
|
29
|
+
SelectContent as OrderSelectContent,
|
|
30
|
+
SelectItem as OrderSelectItem,
|
|
31
|
+
SelectValue as OrderSelectValue,
|
|
32
|
+
} from "@/components/ui/select";
|
|
33
|
+
|
|
34
|
+
type FiltersProps = {
|
|
35
|
+
// values
|
|
36
|
+
title: string;
|
|
37
|
+
engagementId: string;
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
validateOnChain: boolean;
|
|
40
|
+
type: "single-release" | "multi-release" | "all";
|
|
41
|
+
status:
|
|
42
|
+
| "working"
|
|
43
|
+
| "pendingRelease"
|
|
44
|
+
| "released"
|
|
45
|
+
| "resolved"
|
|
46
|
+
| "inDispute"
|
|
47
|
+
| "all";
|
|
48
|
+
minAmount: string;
|
|
49
|
+
maxAmount: string;
|
|
50
|
+
dateRange: DayPickerDateRange;
|
|
51
|
+
formattedRangeLabel: string;
|
|
52
|
+
|
|
53
|
+
// setters
|
|
54
|
+
setTitle: (v: string) => void;
|
|
55
|
+
setEngagementId: (v: string) => void;
|
|
56
|
+
setIsActive: (v: boolean) => void;
|
|
57
|
+
setValidateOnChain: (v: boolean) => void;
|
|
58
|
+
setType: (v: "single-release" | "multi-release" | "all") => void;
|
|
59
|
+
setStatus: (
|
|
60
|
+
v:
|
|
61
|
+
| "working"
|
|
62
|
+
| "pendingRelease"
|
|
63
|
+
| "released"
|
|
64
|
+
| "resolved"
|
|
65
|
+
| "inDispute"
|
|
66
|
+
| "all"
|
|
67
|
+
) => void;
|
|
68
|
+
setMinAmount: (v: string) => void;
|
|
69
|
+
setMaxAmount: (v: string) => void;
|
|
70
|
+
setDateRange: (r: DayPickerDateRange) => void;
|
|
71
|
+
|
|
72
|
+
// actions
|
|
73
|
+
onClearFilters: () => void;
|
|
74
|
+
onRefresh: () => void;
|
|
75
|
+
isRefreshing: boolean;
|
|
76
|
+
|
|
77
|
+
// ordering
|
|
78
|
+
orderBy: "createdAt" | "updatedAt" | "amount";
|
|
79
|
+
orderDirection: "asc" | "desc";
|
|
80
|
+
setOrderBy: (v: "createdAt" | "updatedAt" | "amount") => void;
|
|
81
|
+
setOrderDirection: (v: "asc" | "desc") => void;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
function Filters({
|
|
85
|
+
title,
|
|
86
|
+
engagementId,
|
|
87
|
+
isActive,
|
|
88
|
+
validateOnChain,
|
|
89
|
+
type,
|
|
90
|
+
status,
|
|
91
|
+
minAmount,
|
|
92
|
+
maxAmount,
|
|
93
|
+
dateRange,
|
|
94
|
+
formattedRangeLabel,
|
|
95
|
+
isRefreshing,
|
|
96
|
+
orderBy,
|
|
97
|
+
orderDirection,
|
|
98
|
+
setTitle,
|
|
99
|
+
setEngagementId,
|
|
100
|
+
setIsActive,
|
|
101
|
+
setValidateOnChain,
|
|
102
|
+
setType,
|
|
103
|
+
setStatus,
|
|
104
|
+
setMinAmount,
|
|
105
|
+
setMaxAmount,
|
|
106
|
+
setDateRange,
|
|
107
|
+
onClearFilters,
|
|
108
|
+
onRefresh,
|
|
109
|
+
setOrderBy,
|
|
110
|
+
setOrderDirection,
|
|
111
|
+
}: FiltersProps) {
|
|
112
|
+
return (
|
|
113
|
+
<div className="w-full bg-card/50 backdrop-blur-sm border border-border/50 rounded-lg p-4 shadow-sm">
|
|
114
|
+
{/* Header Section */}
|
|
115
|
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
|
|
116
|
+
<div className="flex items-center gap-2">
|
|
117
|
+
<SlidersHorizontal className="w-4 h-4 text-primary" />
|
|
118
|
+
<h3 className="font-semibold text-foreground">Filters</h3>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div className="flex items-center gap-2">
|
|
122
|
+
<Button
|
|
123
|
+
variant="outline"
|
|
124
|
+
size="sm"
|
|
125
|
+
className="h-8 px-3 text-xs font-medium border-border/60 bg-background/80 hover:bg-muted/80 transition-colors cursor-pointer"
|
|
126
|
+
onClick={onRefresh}
|
|
127
|
+
disabled={isRefreshing}
|
|
128
|
+
>
|
|
129
|
+
<RefreshCcw
|
|
130
|
+
className={`w-3 h-3 ${isRefreshing ? "animate-spin" : ""}`}
|
|
131
|
+
/>
|
|
132
|
+
<span className="hidden xs:inline">Refresh</span>
|
|
133
|
+
</Button>
|
|
134
|
+
|
|
135
|
+
<Button
|
|
136
|
+
variant="ghost"
|
|
137
|
+
size="sm"
|
|
138
|
+
className="h-8 px-3 text-xs font-medium text-destructive hover:text-destructive hover:bg-destructive/10 transition-colors cursor-pointer"
|
|
139
|
+
onClick={onClearFilters}
|
|
140
|
+
>
|
|
141
|
+
<Trash2 className="w-3 h-3" />
|
|
142
|
+
<span className="hidden xs:inline">Clear</span>
|
|
143
|
+
</Button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Filters Grid */}
|
|
148
|
+
<div className="space-y-4">
|
|
149
|
+
{/* Row 1: Search, ID, Amount Range, and Type */}
|
|
150
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
|
151
|
+
<div className="space-y-1.5">
|
|
152
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
153
|
+
Search
|
|
154
|
+
</label>
|
|
155
|
+
<div className="relative">
|
|
156
|
+
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
|
157
|
+
<Input
|
|
158
|
+
placeholder="Search title..."
|
|
159
|
+
value={title}
|
|
160
|
+
onChange={(e) => setTitle(e.target.value)}
|
|
161
|
+
className="h-9 pl-9 text-sm border-border/60 focus:border-primary/60 bg-background/80 transition-colors w-full"
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div className="space-y-1.5">
|
|
167
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
168
|
+
Engagement ID
|
|
169
|
+
</label>
|
|
170
|
+
<div className="relative">
|
|
171
|
+
<FilterIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
|
172
|
+
<Input
|
|
173
|
+
placeholder="Engagement ID"
|
|
174
|
+
value={engagementId}
|
|
175
|
+
onChange={(e) => setEngagementId(e.target.value)}
|
|
176
|
+
className="h-9 pl-9 text-sm border-border/60 focus:border-primary/60 bg-background/80 transition-colors w-full"
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<div className="space-y-1.5">
|
|
182
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
183
|
+
Amount Range
|
|
184
|
+
</label>
|
|
185
|
+
<div className="w-full">
|
|
186
|
+
<div className="flex items-center gap-2 w-full">
|
|
187
|
+
<div className="relative flex-1">
|
|
188
|
+
<DollarSign className="absolute left-2 top-1/2 transform -translate-y-1/2 w-3 h-3 text-muted-foreground" />
|
|
189
|
+
<Input
|
|
190
|
+
placeholder="Min"
|
|
191
|
+
type="number"
|
|
192
|
+
min="0"
|
|
193
|
+
value={minAmount}
|
|
194
|
+
onChange={(e) => setMinAmount(e.target.value)}
|
|
195
|
+
className="h-9 pl-7 text-sm border-border/60 focus:border-primary/60 bg-background/80 w-full"
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
<span className="text-xs text-muted-foreground px-1">-</span>
|
|
199
|
+
<div className="relative flex-1">
|
|
200
|
+
<DollarSign className="absolute left-2 top-1/2 transform -translate-y-1/2 w-3 h-3 text-muted-foreground" />
|
|
201
|
+
<Input
|
|
202
|
+
placeholder="Max"
|
|
203
|
+
type="number"
|
|
204
|
+
min="0"
|
|
205
|
+
value={maxAmount}
|
|
206
|
+
onChange={(e) => setMaxAmount(e.target.value)}
|
|
207
|
+
className="h-9 pl-7 text-sm border-border/60 focus:border-primary/60 bg-background/80 w-full"
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div className="space-y-1.5">
|
|
215
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
216
|
+
Type
|
|
217
|
+
</label>
|
|
218
|
+
<div className="w-full">
|
|
219
|
+
<Select
|
|
220
|
+
value={type}
|
|
221
|
+
onValueChange={(v) => setType(v as typeof type)}
|
|
222
|
+
>
|
|
223
|
+
<SelectTrigger className="h-9 text-sm border-border/60 bg-background/80 w-full">
|
|
224
|
+
<SelectValue placeholder="Select type" />
|
|
225
|
+
</SelectTrigger>
|
|
226
|
+
<SelectContent>
|
|
227
|
+
<SelectItem value="all">All types</SelectItem>
|
|
228
|
+
<SelectItem value="single-release">Single release</SelectItem>
|
|
229
|
+
<SelectItem value="multi-release">Multi release</SelectItem>
|
|
230
|
+
</SelectContent>
|
|
231
|
+
</Select>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
{/* Row 2: Status, Date Range, Active Checkbox, and Ordering */}
|
|
237
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-3 items-end">
|
|
238
|
+
<div className="space-y-1.5">
|
|
239
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
240
|
+
Status
|
|
241
|
+
</label>
|
|
242
|
+
<div className="w-full">
|
|
243
|
+
<Select
|
|
244
|
+
value={status}
|
|
245
|
+
onValueChange={(v) => setStatus(v as typeof status)}
|
|
246
|
+
>
|
|
247
|
+
<SelectTrigger className="h-9 text-sm border-border/60 bg-background/80 w-full">
|
|
248
|
+
<SelectValue placeholder="Select status" />
|
|
249
|
+
</SelectTrigger>
|
|
250
|
+
<SelectContent>
|
|
251
|
+
<SelectItem value="all">All statuses</SelectItem>
|
|
252
|
+
<SelectItem value="working">Working</SelectItem>
|
|
253
|
+
<SelectItem value="pendingRelease">
|
|
254
|
+
Pending release
|
|
255
|
+
</SelectItem>
|
|
256
|
+
<SelectItem value="released">Released</SelectItem>
|
|
257
|
+
<SelectItem value="resolved">Resolved</SelectItem>
|
|
258
|
+
<SelectItem value="inDispute">In dispute</SelectItem>
|
|
259
|
+
</SelectContent>
|
|
260
|
+
</Select>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div className="space-y-1.5">
|
|
265
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
266
|
+
Date Range
|
|
267
|
+
</label>
|
|
268
|
+
<div className="w-full">
|
|
269
|
+
<Popover>
|
|
270
|
+
<PopoverTrigger asChild>
|
|
271
|
+
<Button
|
|
272
|
+
variant="outline"
|
|
273
|
+
size="sm"
|
|
274
|
+
className="h-9 text-sm border-border/60 bg-background/80 hover:bg-muted/80 w-full justify-start transition-colors"
|
|
275
|
+
>
|
|
276
|
+
<CalendarIcon className="w-4 h-4 mr-2 flex-shrink-0" />
|
|
277
|
+
<span className="truncate">{formattedRangeLabel}</span>
|
|
278
|
+
</Button>
|
|
279
|
+
</PopoverTrigger>
|
|
280
|
+
<PopoverContent className="w-auto p-0" align="start">
|
|
281
|
+
<div className="p-3">
|
|
282
|
+
<Calendar
|
|
283
|
+
mode="range"
|
|
284
|
+
selected={dateRange}
|
|
285
|
+
onSelect={(range: DayPickerDateRange | undefined) =>
|
|
286
|
+
setDateRange(
|
|
287
|
+
range ?? { from: undefined, to: undefined }
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
numberOfMonths={2}
|
|
291
|
+
/>
|
|
292
|
+
<div className="mt-3 flex justify-end">
|
|
293
|
+
<Button
|
|
294
|
+
variant="ghost"
|
|
295
|
+
size="sm"
|
|
296
|
+
onClick={() =>
|
|
297
|
+
setDateRange({ from: undefined, to: undefined })
|
|
298
|
+
}
|
|
299
|
+
>
|
|
300
|
+
Clear
|
|
301
|
+
</Button>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
</PopoverContent>
|
|
305
|
+
</Popover>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div className="space-y-1.5">
|
|
310
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
311
|
+
Active / OnChain
|
|
312
|
+
</label>
|
|
313
|
+
<div className="w-full lg:w-auto lg:min-w-fit">
|
|
314
|
+
<div className="grid grid-cols-2 gap-2 w-full lg:w-auto">
|
|
315
|
+
<div className="flex items-center justify-center lg:justify-start gap-2 h-9 px-3 rounded-md border border-border/60 bg-background/80 w-full lg:w-auto">
|
|
316
|
+
<Checkbox
|
|
317
|
+
checked={Boolean(isActive)}
|
|
318
|
+
onCheckedChange={(checked) => setIsActive(Boolean(checked))}
|
|
319
|
+
/>
|
|
320
|
+
<span className="text-sm text-foreground font-medium whitespace-nowrap">
|
|
321
|
+
Active
|
|
322
|
+
</span>
|
|
323
|
+
</div>
|
|
324
|
+
<div className="flex items-center justify-center lg:justify-start gap-2 h-9 px-3 rounded-md border border-border/60 bg-background/80 w-full lg:w-auto">
|
|
325
|
+
<Checkbox
|
|
326
|
+
checked={Boolean(validateOnChain)}
|
|
327
|
+
onCheckedChange={(checked) =>
|
|
328
|
+
setValidateOnChain(Boolean(checked))
|
|
329
|
+
}
|
|
330
|
+
/>
|
|
331
|
+
<span className="text-sm text-foreground font-medium whitespace-nowrap">
|
|
332
|
+
OnChain
|
|
333
|
+
</span>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<div className="space-y-1.5 w-full lg:col-span-2">
|
|
340
|
+
<label className="text-xs font-medium text-muted-foreground">
|
|
341
|
+
Sort By
|
|
342
|
+
</label>
|
|
343
|
+
<div className="w-full">
|
|
344
|
+
<div className="flex items-center gap-2 w-full">
|
|
345
|
+
<div className="flex-1">
|
|
346
|
+
<OrderSelect
|
|
347
|
+
value={orderBy}
|
|
348
|
+
onValueChange={(v) => setOrderBy(v as typeof orderBy)}
|
|
349
|
+
>
|
|
350
|
+
<OrderSelectTrigger className="h-9 text-sm border-border/60 bg-background/80 w-full">
|
|
351
|
+
<OrderSelectValue placeholder="Order by" />
|
|
352
|
+
</OrderSelectTrigger>
|
|
353
|
+
<OrderSelectContent>
|
|
354
|
+
<OrderSelectItem value="createdAt">
|
|
355
|
+
Created
|
|
356
|
+
</OrderSelectItem>
|
|
357
|
+
<OrderSelectItem value="updatedAt">
|
|
358
|
+
Updated
|
|
359
|
+
</OrderSelectItem>
|
|
360
|
+
<OrderSelectItem value="amount">Amount</OrderSelectItem>
|
|
361
|
+
</OrderSelectContent>
|
|
362
|
+
</OrderSelect>
|
|
363
|
+
</div>
|
|
364
|
+
<div className="flex-1">
|
|
365
|
+
<OrderSelect
|
|
366
|
+
value={orderDirection}
|
|
367
|
+
onValueChange={(v) =>
|
|
368
|
+
setOrderDirection(v as typeof orderDirection)
|
|
369
|
+
}
|
|
370
|
+
>
|
|
371
|
+
<OrderSelectTrigger className="h-9 text-sm border-border/60 bg-background/80 w-full">
|
|
372
|
+
<OrderSelectValue placeholder="Direction" />
|
|
373
|
+
</OrderSelectTrigger>
|
|
374
|
+
<OrderSelectContent>
|
|
375
|
+
<OrderSelectItem value="desc">Descending</OrderSelectItem>
|
|
376
|
+
<OrderSelectItem value="asc">Ascending</OrderSelectItem>
|
|
377
|
+
</OrderSelectContent>
|
|
378
|
+
</OrderSelect>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export default React.memo(Filters);
|