Skip to content

Commit 22f29b5

Browse files
committed
feat: add DealBreadcrumbs and DealDetailsTable components for improved deal detail presentation
1 parent fec2c21 commit 22f29b5

File tree

4 files changed

+256
-207
lines changed

4 files changed

+256
-207
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {
2+
Breadcrumb,
3+
BreadcrumbItem,
4+
BreadcrumbLink,
5+
BreadcrumbList,
6+
BreadcrumbPage,
7+
BreadcrumbSeparator,
8+
} from '@/components/ui/breadcrumb';
9+
10+
type DealBreadcrumbsProps = {
11+
dealId: string;
12+
};
13+
14+
export function DealBreadcrumbs({ dealId }: DealBreadcrumbsProps) {
15+
return (
16+
<Breadcrumb>
17+
<BreadcrumbList>
18+
<BreadcrumbItem>
19+
<BreadcrumbLink href="/">Homepage</BreadcrumbLink>
20+
</BreadcrumbItem>
21+
<BreadcrumbSeparator />
22+
<BreadcrumbItem>
23+
<BreadcrumbLink href="/deals">All deals</BreadcrumbLink>
24+
</BreadcrumbItem>
25+
<BreadcrumbSeparator />
26+
<BreadcrumbItem>
27+
<BreadcrumbPage>Deal {dealId}</BreadcrumbPage>
28+
</BreadcrumbItem>
29+
</BreadcrumbList>
30+
</Breadcrumb>
31+
);
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// components/DealDetailsTable.tsx
2+
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
3+
4+
type DealDetailsTableProps = {
5+
details: Record<string, React.ReactNode | React.ReactNode[]>;
6+
};
7+
8+
export function DealDetailsTable({ details }: DealDetailsTableProps) {
9+
return (
10+
<Table>
11+
<TableBody>
12+
{Object.entries(details).map(([key, value], i) => (
13+
<TableRow key={i}>
14+
<TableCell>{key} :</TableCell>
15+
<TableCell>
16+
{Array.isArray(value)
17+
? value.map((v, j) => <div key={j}>{v}</div>)
18+
: value}
19+
</TableCell>
20+
</TableRow>
21+
))}
22+
</TableBody>
23+
</Table>
24+
);
25+
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import { Link } from '@tanstack/react-router';
2+
import CopyButton from '@/components/CopyButton';
3+
import { Button } from '@/components/ui/button';
4+
import { formatElapsedTime } from '@/utils/formatElapsedTime';
5+
import { Deal } from '@/graphql/graphql';
6+
7+
export function buildDealDetails({
8+
deal,
9+
isConnected,
10+
}: {
11+
deal: Deal;
12+
isConnected: boolean;
13+
}) {
14+
const dealDeadline =
15+
deal?.startTime &&
16+
deal?.category.workClockTimeRef &&
17+
parseInt(deal?.startTime) + parseInt(deal?.category.workClockTimeRef) * 10;
18+
19+
const tasksCount = parseInt(deal?.botSize) || 1;
20+
const completedTasksCount = parseInt(deal?.completedTasksCount) || 0;
21+
const claimedTasksCount = parseInt(deal?.claimedTasksCount) || 0;
22+
const pendingTasksCount =
23+
tasksCount - (completedTasksCount + claimedTasksCount);
24+
25+
const completedRatio = completedTasksCount / tasksCount;
26+
const claimedRatio = claimedTasksCount / tasksCount;
27+
const pendingRatio = pendingTasksCount / tasksCount;
28+
29+
const isClaimable =
30+
dealDeadline < Date.now() && isConnected && pendingRatio > 0;
31+
32+
return {
33+
...(deal.dealid && {
34+
Dealid: (
35+
<div>
36+
<Button variant="link" className="text-sm">
37+
<Link to={`/deals/${deal.dealid}`}>{deal.dealid}</Link>
38+
</Button>
39+
<CopyButton textToCopy={deal.dealid} />
40+
</div>
41+
),
42+
}),
43+
...(deal.category.catid !== undefined &&
44+
deal.category.workClockTimeRef !== undefined && {
45+
Category: (
46+
<>
47+
{deal.category.catid} ({deal.category.workClockTimeRef * 10} sec)
48+
</>
49+
),
50+
}),
51+
// ...(deal.tag && {
52+
// Tag: <Bytes>{deal.tag}</Bytes>,
53+
// }),
54+
...(deal.trust && {
55+
Trust: deal.trust,
56+
}),
57+
...(deal.app && {
58+
App: (
59+
<>
60+
{deal.app?.name}{' '}
61+
<Button variant="link" className="text-sm">
62+
<Link to={`/apps/${deal.app?.address}`}>{deal.dealid}</Link>
63+
</Button>
64+
</>
65+
),
66+
'App price': deal.appPrice,
67+
}),
68+
...(deal.dataset && {
69+
Dataset: (
70+
<>
71+
{deal.app.name}{' '}
72+
<Button variant="link" className="text-sm">
73+
<Link to={`/datasets/${deal.dataset?.address}`}>{deal.dealid}</Link>
74+
</Button>
75+
</>
76+
),
77+
'Dataset price': deal.datasetPrice,
78+
}),
79+
...(deal.workerpool && {
80+
Workerpool: (
81+
<>
82+
{deal.workerpool.description}{' '}
83+
<Button variant="link" className="text-sm">
84+
<Link to={`/workerpool/${deal.workerpool.address}`}>
85+
{deal.dealid}
86+
</Link>
87+
</Button>
88+
</>
89+
),
90+
'Workerpool price': deal.workerpoolPrice,
91+
}),
92+
...(deal.requester && {
93+
Requester: (
94+
<Button variant="link" className="text-sm">
95+
<Link to={`/address/${deal.requester.address}`}>{deal.dealid}</Link>
96+
</Button>
97+
),
98+
}),
99+
...(deal.beneficiary && {
100+
Beneficiary: (
101+
<Button variant="link" className="text-sm">
102+
<Link to={`/address/${deal.beneficiary.address}`}>{deal.dealid}</Link>
103+
</Button>
104+
),
105+
}),
106+
...(deal.callback && {
107+
'Callback contract': (
108+
<Button variant="link" className="text-sm">
109+
<Link to={`/address/${deal.callback.address}`}>
110+
{deal.callback.address}
111+
</Link>
112+
</Button>
113+
),
114+
}),
115+
...(deal.params && {
116+
Params: (
117+
<div>
118+
<pre className="inline-block whitespace-pre">
119+
{JSON.stringify(JSON.parse(deal.params), null, 2)}
120+
</pre>
121+
<CopyButton textToCopy={deal.dealid} />
122+
</div>
123+
),
124+
}),
125+
...((pendingRatio || completedRatio || claimedRatio) && {
126+
Status: (
127+
<span>
128+
{completedRatio > 0 && <>{completedRatio * 100}% COMPLETED</>}
129+
{completedRatio > 0 && claimedRatio > 0 && <> - </>}
130+
{claimedRatio > 0 && <>{claimedRatio * 100}% CLAIMED</>}
131+
{claimedRatio > 0 && pendingRatio > 0 && <> - </>}
132+
{pendingRatio > 0 && (
133+
<>
134+
{pendingRatio * 100}% {isClaimable ? 'CLAIMABLE' : 'PENDING'}
135+
</>
136+
)}
137+
{/* {isClaimable && (
138+
<ActionLink action={onClaim}>Claim failed tasks</ActionLink>
139+
)} */}
140+
</span>
141+
),
142+
}),
143+
...(deal.botSize && {
144+
'Tasks count': deal.botSize,
145+
}),
146+
...(deal.completedTasksCount && {
147+
'Completed tasks count': deal.completedTasksCount,
148+
}),
149+
...(deal.claimedTasksCount && {
150+
'Claimed tasks count': deal.claimedTasksCount,
151+
}),
152+
...(deal.startTime && {
153+
'Start time': (
154+
<>
155+
{formatElapsedTime(parseInt(deal.startTime))} (
156+
{new Date(deal.startTime * 1000).toString()})
157+
</>
158+
),
159+
}),
160+
...(dealDeadline && {
161+
Deadline: (
162+
<>
163+
{formatElapsedTime(parseInt(dealDeadline))} (
164+
{new Date(dealDeadline * 1000).toString()})
165+
</>
166+
),
167+
}),
168+
...(Array.isArray(deal?.dealEvents) && {
169+
Events: deal.dealEvents.map((dealEvent, i) => (
170+
<div key={i}>
171+
{dealEvent.type && <>{dealEvent.type} </>}
172+
{dealEvent.transaction.txHash && (
173+
<>
174+
<Button variant="link" className="text-sm">
175+
<Link to={`/transaction/${dealEvent.transaction.txHash}`}>
176+
{dealEvent.transaction.txHash}
177+
</Link>
178+
</Button>
179+
<Button variant="link" className="text-sm">
180+
<Link to={`/tx/${dealEvent.transaction.txHash}`}>
181+
{dealEvent.transaction.txHash}
182+
</Link>
183+
</Button>
184+
</>
185+
)}
186+
</div>
187+
)),
188+
}),
189+
};
190+
}

0 commit comments

Comments
 (0)