Skip to content

Commit 5e08ad5

Browse files
feat: upgrade access feature (#91)
* feat: add tab navigation for app details and deals in AppsRoute component * feat: add AppAccessTable component and integrate into AppsRoute * feat: enhance AppAccessTable and AppDealsTable with loading and outdated state management * feat: rename columns to appColumns and remove app address and price column * feat: add tab navigation for dataset details, deals, and access in DatasetsRoute * feat: integrate loading and outdated state management in DatasetDealsTable * feat: add DatasetAccessTable component and integrate it into DatasetsRoute * feat: add tab navigation for workerpool details, deals, and access * feat: enhance WorkerpoolDealsTable with loading and outdated state management * feat: add WorkerpoolAccessTable component and integrate it into WorkerpoolsRoute * feat: refactor loading and outdated state management in access and deals tables using useEffect * feat: add access tables for apps, datasets, and workerpools on address page * fix: correct import path for WorkerpoolAccessTable component * refactor: remove console logs from access data fetching functions * feat: enhance pagination logic to display all pages for 7 or fewer total pages * feat: update pagination logic to support mobile-first design and dynamic page visibility * feat: update access data fetching logic to support pagination and total count for apps, datasets, and workerpools * fix: prevent rendering of pagination controls for invalid states * refactor: prettier * fix: stabilize pagination rendering during lading state * fix: normalize case for order restrictions in CopyButton components * fix: useTabParam() name in dataset Co-authored-by: Copilot <[email protected]> * fix: correct error message wording in loading alerts across multiple tables * feat: add price column with token symbol for app, dataset, and workerpool orders * fix: conditionally apply cursor pointer class based on destination * feat: format access data to include destination paths for apps, datasets, and workerpools * fix: adjust indicator position calculation to include scroll offset * fix: update initIExecSDKs to work without wallet connection * fix: convert price values from NRLC to RLC across app, dataset, and workerpool access columns * fix: update orderbook fetch calls to include 'any' for dataset, app, and workerpool parameters * fix: improve pagination stability by updating totalPages reference condition * feat: rename "access" tables to "access from" * fix: remove unused datasetOwner parameter from dataset orderbook fetch * fix: update query keys to use 'accessFrom' for apps, datasets, and workerpools * feat: add access to tables for apps, datasets, and workerpools to address layout * fix: stabilize pagination by resetting totalPages reference on chainId change * fix: remove debug log for provider in initIExecSDKs function * fix: include 'requester' parameter as 'any' in access data queries for app, dataset, and workerpool * fix: reorder AddressAppsAccessTable in AddressRoute component * fix: reorder AddressAppsAccessFromTable in AddressRoute component * feat: rename access table "access from/to" to "granted/received access" * feat: add Access page * feat: convert dataset prices to RLC format in buildAccessDetails function * feat: update access destination paths to use orderHash for all access tables * fix: remove unnecessary refetchInterval and console log in useAccessData * feat: enhance buildAccessDetails function with type-specific access handling * fix: add 'order' type to LinkType in SmartLinkGroup usage in buildAccessDetails * feat: implement revoke access functionality for app, dataset, and workerpool orders * fix: adjust padding in tab buttons for improved layout * fix: update comment to reflect correct number of displayed items per page in access tables * fix: remove unnecessary console log and adjust access check placement in RevokeAccess component * fix: remove unused imports for access tables in address layout * feat: update access routes and components for apps, datasets, and workerpools * refactor: remove access hash route and related components * feat: enhance SmartLinkGroup to conditionally display address or ID and label; update access details functions to include app, dataset, and workerpool information * feat: add revoke access button on user access granted page * fix: update remaining display to show remaining and total volume in access details * fix: update access destination path to include dataset in DatasetAccessTable * feat: implement access assets details * feat: reorder access details display * feat: update restriction display to show 'Not restricted' for zero address --------- Co-authored-by: Copilot <[email protected]>
1 parent 94f3195 commit 5e08ad5

30 files changed

+2393
-106
lines changed

src/components/SmartLinkGroup.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,23 @@ type LinkType =
2222
| 'workerpool'
2323
| 'app'
2424
| 'address'
25-
| 'transaction';
25+
| 'transaction'
26+
| 'order';
2627

2728
interface SmartLinkGroupProps {
2829
type: LinkType;
2930
addressOrId: string;
3031
label?: string;
3132
isCurrentPage?: boolean;
33+
showAddressOrIdAndLabel?: boolean;
3234
}
3335

3436
export default function SmartLinkGroup({
3537
type,
3638
addressOrId,
3739
label,
3840
isCurrentPage = false,
41+
showAddressOrIdAndLabel = false,
3942
}: SmartLinkGroupProps) {
4043
const { chainId, isConnected } = useUserStore();
4144
const basePath = {
@@ -46,6 +49,7 @@ export default function SmartLinkGroup({
4649
app: 'app',
4750
address: 'address',
4851
transaction: 'tx',
52+
order: 'order',
4953
};
5054

5155
const { data: ens } = useQuery({
@@ -78,12 +82,15 @@ export default function SmartLinkGroup({
7882
<Link
7983
to={`/${getChainFromId(chainId)?.slug}/${basePath[type]}/${addressOrId}`}
8084
>
81-
<span className="hidden md:inline">{label ?? addressOrId}</span>
85+
<span className="hidden md:inline">
86+
{label && !showAddressOrIdAndLabel ? label : addressOrId}
87+
</span>
8288
<span className="inline md:hidden">
8389
{(label
8490
? truncateAddress(label)
8591
: truncateAddress(addressOrId)) ?? addressOrId}
8692
</span>
93+
{showAddressOrIdAndLabel && label ? `(${label})` : ''}
8794
{ens ? `(${ens})` : ''}
8895
</Link>
8996
</Button>
@@ -98,7 +105,7 @@ export default function SmartLinkGroup({
98105
</div>
99106
)}
100107

101-
{type !== 'task' && (
108+
{type !== 'task' && type !== 'order' && (
102109
<TooltipProvider delayDuration={0}>
103110
<Tooltip>
104111
<TooltipTrigger asChild>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const DatasetIcon = ({ size = 20, className = '' }) => (
2+
<svg
3+
width={size}
4+
height={size}
5+
viewBox="0 0 24 25"
6+
fill="none"
7+
xmlns="http://www.w3.org/2000/svg"
8+
className={className}
9+
>
10+
<path
11+
d="M12 9.16691C16.1421 9.16691 19.5 8.04764 19.5 6.66695C19.5 5.28626 16.1421 4.16699 12 4.16699C7.85786 4.16699 4.5 5.28626 4.5 6.66695C4.5 8.04764 7.85786 9.16691 12 9.16691Z"
12+
stroke="currentColor"
13+
strokeWidth="2.4"
14+
strokeLinecap="round"
15+
strokeLinejoin="round"
16+
/>
17+
<path
18+
d="M19.5 12.5C19.5 13.8834 16.1666 15 12 15C7.83336 15 4.5 13.8832 4.5 12.5"
19+
stroke="currentColor"
20+
strokeWidth="2.4"
21+
strokeLinecap="round"
22+
strokeLinejoin="round"
23+
/>
24+
<path
25+
d="M4.5 6.66602V18.3327C4.5 19.716 7.83336 20.8326 12 20.8326C16.1666 20.8326 19.5 19.7159 19.5 18.3327V6.66602"
26+
stroke="currentColor"
27+
strokeWidth="2.4"
28+
strokeLinecap="round"
29+
strokeLinejoin="round"
30+
/>
31+
</svg>
32+
);
33+
34+
export default DatasetIcon;

src/modules/Tabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function Tabs({
7878
if (!isDisabled) onTabChange(index);
7979
}}
8080
className={cn(
81-
'text-foreground relative z-10 border border-transparent px-8 py-2 transition-colors duration-300 hover:no-underline',
81+
'text-foreground relative z-10 border border-transparent px-4 py-2 transition-colors duration-300 hover:no-underline',
8282
isDisabled && 'cursor-not-allowed opacity-50',
8383
currentTab === index && 'text-primary'
8484
)}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ChainLink } from '@/components/ChainLink';
2+
import {
3+
Breadcrumb,
4+
BreadcrumbItem,
5+
BreadcrumbLink,
6+
BreadcrumbList,
7+
BreadcrumbPage,
8+
BreadcrumbSeparator,
9+
} from '@/components/ui/breadcrumb';
10+
import { truncateAddress } from '@/utils/truncateAddress';
11+
12+
type AccessBreadcrumbsProps = {
13+
accessHash: string;
14+
};
15+
16+
export function AccessBreadcrumbs({ accessHash }: AccessBreadcrumbsProps) {
17+
return (
18+
<Breadcrumb>
19+
<BreadcrumbList>
20+
<BreadcrumbItem>
21+
<BreadcrumbLink asChild>
22+
<ChainLink to="/">Homepage</ChainLink>
23+
</BreadcrumbLink>
24+
</BreadcrumbItem>
25+
<BreadcrumbSeparator />
26+
<BreadcrumbItem>
27+
<BreadcrumbPage>
28+
Access{' '}
29+
<span className="font-normal">{truncateAddress(accessHash)}</span>
30+
</BreadcrumbPage>
31+
</BreadcrumbItem>
32+
</BreadcrumbList>
33+
</Breadcrumb>
34+
);
35+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { useMutation, useQueryClient } from '@tanstack/react-query';
2+
import { useNavigate } from '@tanstack/react-router';
3+
import {
4+
PublishedApporder,
5+
PublishedDatasetorder,
6+
PublishedWorkerpoolorder,
7+
} from 'iexec/IExecOrderbookModule';
8+
import { LoaderCircle } from 'lucide-react';
9+
import { Button } from '@/components/ui/button';
10+
import { getIExec } from '@/externals/iexecSdkClient';
11+
import useUserStore from '@/stores/useUser.store';
12+
import { getChainFromId } from '@/utils/chain.utils';
13+
14+
export default function RevokeAccess({
15+
access,
16+
onRevoked,
17+
}: {
18+
access: PublishedApporder | PublishedDatasetorder | PublishedWorkerpoolorder;
19+
onRevoked?: () => void;
20+
}) {
21+
const { chainId, address: userAddress } = useUserStore();
22+
const queryClient = useQueryClient();
23+
const navigate = useNavigate();
24+
25+
const order = access.order;
26+
27+
const revokeAccessMutation = useMutation({
28+
mutationFn: async () => {
29+
const iexec = await getIExec();
30+
31+
if ('app' in order) {
32+
return await iexec.order.cancelApporder(order);
33+
} else if ('dataset' in order) {
34+
return await iexec.order.cancelDatasetorder(order);
35+
} else if ('workerpool' in order) {
36+
return await iexec.order.cancelWorkerpoolorder(order);
37+
} else {
38+
throw new Error('Unknown order type');
39+
}
40+
},
41+
onError: (err) => {
42+
console.error(err);
43+
},
44+
onSuccess: () => {
45+
let accessType = '';
46+
if ('app' in order) {
47+
accessType = 'App';
48+
} else if ('dataset' in order) {
49+
accessType = 'Dataset';
50+
} else {
51+
accessType = 'Workerpool';
52+
}
53+
queryClient.invalidateQueries({
54+
queryKey: [chainId, 'address', `${accessType}sGrantedAccess`],
55+
});
56+
// Navigate to the user's granted access tab if we have user address
57+
if (userAddress) {
58+
const chainSlug = chainId ? getChainFromId(chainId)?.slug : undefined;
59+
if (chainSlug) {
60+
navigate({
61+
to: `/${chainSlug}/address/${userAddress}`,
62+
search: { addressTab: 'GRANTED ACCESS' },
63+
replace: true,
64+
});
65+
}
66+
}
67+
if (onRevoked) onRevoked();
68+
},
69+
});
70+
71+
if (!access) return;
72+
73+
return (
74+
<Button
75+
variant="outline"
76+
size="sm"
77+
onClick={() => revokeAccessMutation.mutate()}
78+
disabled={revokeAccessMutation.isPending}
79+
>
80+
{revokeAccessMutation.isPending && (
81+
<LoaderCircle className="animate-spin" />
82+
)}
83+
Revoke access
84+
</Button>
85+
);
86+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { graphql } from '@/graphql/poco/gql';
2+
3+
export const accessAssetsDetailsQuery = graphql(`
4+
query AccessAssetsDetails(
5+
$datasetAddress: ID!
6+
$appAddress: ID!
7+
$workerpoolAddress: ID!
8+
$categoryId: ID!
9+
) {
10+
dataset(id: $datasetAddress) {
11+
name
12+
}
13+
app(id: $appAddress) {
14+
name
15+
}
16+
workerpool(id: $workerpoolAddress) {
17+
description
18+
}
19+
category(id: $categoryId) {
20+
description
21+
id
22+
name
23+
workClockTimeRef
24+
}
25+
}
26+
`);

0 commit comments

Comments
 (0)