Skip to content

Commit 9b26ae6

Browse files
committed
improve roles management
1 parent 1cf802b commit 9b26ae6

11 files changed

Lines changed: 227 additions & 95 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### [0.11.2](https://github.com/xdevguild/buildo.dev/releases/tag/v0.11.2) (2023-12-31)
2+
- improve roles and properties selectors to make them less confusing
3+
14
### [0.11.1](https://github.com/xdevguild/buildo.dev/releases/tag/v0.11.1) (2023-12-29)
25
- update dependencies (some improvements in useElven)
36

components/operations/common/toggle-special-roles.tsx

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
ContractCallPayloadBuilder,
88
ContractFunction,
99
} from '@multiversx/sdk-core';
10-
import { useForm } from 'react-hook-form';
10+
import { useForm, useWatch } from 'react-hook-form';
1111

1212
import { zodResolver } from '@hookform/resolvers/zod';
1313
import { Form } from '@/components/ui/form';
@@ -27,11 +27,15 @@ import {
2727
} from '@/components/operations/constants';
2828
import { OperationsInputField } from '@/components/operations/operations-input-field';
2929
import { OperationsCheckboxGroup } from '@/components/operations/operations-checkbox-group';
30-
import { OperationsSubmitButton } from '../operations-submit-button';
31-
import { useContext } from 'react';
30+
import { OperationsSubmitButton } from '@/components/operations/operations-submit-button';
31+
import { useContext, useEffect, useState } from 'react';
3232
import { OperationsStateDialogContext } from '@/components/operations/operations-status-dialog';
3333
import { CommonOpertationContentProps } from '@/components/operations/operations-common-types';
34-
import { OperationsRadioGroup } from '../operations-radio-group';
34+
import { OperationsRadioGroup } from '@/components/operations/operations-radio-group';
35+
import { useCreatorTokens } from '@/hooks/use-creator-tokens';
36+
import { OperationsSelectField } from '@/components/operations/operations-select-field';
37+
import { useAccount } from '@useelven/core';
38+
import { useTokenRolesByAccount } from '@/hooks/use-token-roles-by-account';
3539

3640
const formSchema = z.object({
3741
tokenId: z.string().min(1, 'The field is required'),
@@ -57,20 +61,54 @@ export const ToggleSpecialRoles = ({
5761
close,
5862
tokenType,
5963
}: CommonOpertationContentProps) => {
64+
const { address } = useAccount();
6065
const { setOpen: setTxStatusDialogOpen } = useContext(
6166
OperationsStateDialogContext
6267
);
6368

69+
const [disabledRoles, setDisabledRoles] = useState<string[]>();
70+
71+
const { tokens } = useCreatorTokens<{ ticker: string }>({ tokenType });
72+
6473
const form = useForm<z.infer<typeof formSchema>>({
6574
resolver: zodResolver(formSchema),
6675
defaultValues: {
6776
tokenId: '',
68-
address: '',
77+
address,
6978
type: 'set',
70-
roles: ['ESDTRoleNFTCreate', 'ESDTRoleNFTBurn'],
79+
roles: [],
7180
},
7281
});
7382

83+
const watchTokenId = useWatch({ name: 'tokenId', control: form.control });
84+
const watchAddress = useWatch({ name: 'address', control: form.control });
85+
const watchType = useWatch({ name: 'type', control: form.control });
86+
87+
const { roles } = useTokenRolesByAccount({
88+
tokenId: watchTokenId,
89+
tokenType,
90+
address: watchAddress,
91+
});
92+
93+
// Handle selected roles in form
94+
useEffect(() => {
95+
const roleNames = rolesMap[tokenType].map((role) => role.name);
96+
97+
if (!watchTokenId || !watchAddress) {
98+
setDisabledRoles(roleNames);
99+
return;
100+
}
101+
102+
let disabledRoles: string[] = [];
103+
if (watchType === 'set') {
104+
disabledRoles = roles;
105+
} else {
106+
disabledRoles = roleNames.filter((role) => !roles?.includes(role));
107+
}
108+
setDisabledRoles(disabledRoles);
109+
// eslint-disable-next-line react-hooks/exhaustive-deps
110+
}, [roles, watchType, watchTokenId, watchAddress]);
111+
74112
const onSubmit = ({
75113
tokenId,
76114
address,
@@ -109,6 +147,17 @@ export const ToggleSpecialRoles = ({
109147
close();
110148
};
111149

150+
const rolesDescription = () => {
151+
if (!watchAddress || !watchTokenId) {
152+
return 'Please set tokenId and address, then you can choose roles!';
153+
}
154+
return `Disabled ones are ${
155+
form.getValues('type') === 'set'
156+
? "set, so you can't set them"
157+
: "not set, so you can't unset them"
158+
}.`;
159+
};
160+
112161
return (
113162
<>
114163
<DialogHeader className="p-8 pb-0">
@@ -137,23 +186,31 @@ export const ToggleSpecialRoles = ({
137186
label="Operation type"
138187
description="Please choose the type of the operation. Set or Unset."
139188
/>
140-
<OperationsInputField
189+
<OperationsSelectField
141190
name="tokenId"
142191
label="Token id"
143-
placeholder="Example: MyToken-23432"
144-
description="Please provide your token id"
192+
description="Example: MyToken-23432"
193+
options={
194+
tokens
195+
? tokens?.map((token) => ({
196+
value: token.ticker,
197+
label: token.ticker,
198+
}))
199+
: []
200+
}
145201
/>
146202
<OperationsInputField
147203
name="address"
148204
label="Address"
149205
placeholder="Example: erd1..."
150-
description="Please provide the address for which the roles will be assigned"
206+
description="Please provide the address for which the roles will be assigned. By default your own."
151207
/>
152208
<OperationsCheckboxGroup
153209
items={rolesMap[tokenType]}
210+
disabledItems={disabledRoles}
154211
name="roles"
155212
label="Roles"
156-
description="Special roles available for basic ESDT tokens."
213+
description={`Special roles available for basic ESDT tokens. ${rolesDescription()}`}
157214
/>
158215
</div>
159216
</form>

components/operations/constants.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ export interface TokenPropertyOrRole {
1818
}
1919

2020
export const esdtTokenProperties: TokenPropertyOrRole[] = [
21+
{
22+
name: 'canAddSpecialRoles',
23+
description:
24+
'The token manager can assign a specific role(s). Important for token management.',
25+
},
26+
{
27+
name: 'canUpgrade',
28+
description:
29+
"The token manager may change these properties. Without it you won't be able to change these properties in the future",
30+
},
2131
{
2232
name: 'canFreeze',
2333
description:
@@ -37,14 +47,6 @@ export const esdtTokenProperties: TokenPropertyOrRole[] = [
3747
name: 'canChangeOwner',
3848
description: 'Token management can be transferred to a different account',
3949
},
40-
{
41-
name: 'canUpgrade',
42-
description: 'The token manager may change these properties',
43-
},
44-
{
45-
name: 'canAddSpecialRoles',
46-
description: 'The token manager can assign a specific role(s)',
47-
},
4850
// TODO: not available yet
4951
// {
5052
// name: 'canCreateMultiShard',
@@ -57,46 +59,47 @@ export const sftNftTokenProperties: TokenPropertyOrRole[] = [
5759
...esdtTokenProperties,
5860
{
5961
name: 'canTransferNFTCreateRole',
60-
description: 'the token manager can transfer NFT/SFT/Meta creation role',
62+
description: 'The token manager can transfer NFT/SFT/Meta creation role',
6163
},
6264
];
6365

6466
const ESDTTransferRole = {
6567
name: 'ESDTTransferRole',
6668
description:
67-
'this role restricts transferability of the token only to the addresses that have the role set, while these addresses can send to any address',
69+
'The role restricts transferability of the token only to the addresses that have the role set, while these addresses can send to any address.',
6870
};
6971

7072
const ESDTRoleNFTCreate = {
7173
name: 'ESDTRoleNFTCreate',
72-
description: 'this role allows one to create a new NFT/SFT/Meta',
74+
description: 'The role allows one to create a new NFT/SFT/Meta',
7375
};
7476

7577
const ESDTRoleNFTBurn = {
7678
name: 'ESDTRoleNFTBurn',
7779
description:
78-
'this role allows one to burn quantity of a specific NFT/SFT/Meta',
80+
'The role allows one to burn quantity of a specific NFT/SFT/Meta',
7981
};
8082

8183
export const esdtTokenSpecialRoles: TokenPropertyOrRole[] = [
8284
{
8385
name: 'ESDTRoleLocalBurn',
84-
description: 'an address with this role can burn tokens',
86+
description: 'An address with this role can burn tokens',
8587
},
8688
{
8789
name: 'ESDTRoleLocalMint',
88-
description: 'an address with this role can mint new tokens',
90+
description: 'An address with this role can mint new tokens',
8991
},
9092
ESDTTransferRole,
9193
];
9294

9395
export const sftTokenSpecialRoles = [
9496
ESDTRoleNFTCreate,
95-
ESDTRoleNFTBurn,
9697
{
9798
name: 'ESDTRoleNFTAddQuantity',
98-
description: 'this role allows one to add quantity of a specific SFT',
99+
description:
100+
'The role allows one to add quantity of a specific SFT/Meta. Also required when creating.',
99101
},
102+
ESDTRoleNFTBurn,
100103
ESDTTransferRole,
101104
];
102105

@@ -106,11 +109,11 @@ export const nftTokenSpecialRoles = [
106109
{
107110
name: 'ESDTRoleNFTUpdateAttributes',
108111
description:
109-
'this role allows one to change the attributes of a specific NFT',
112+
'The role allows one to change the attributes of a specific NFT',
110113
},
111114
{
112115
name: 'ESDTRoleNFTAddURI',
113-
description: 'this role allows one add URIs for a specific NFT',
116+
description: 'The role allows one add URIs for a specific NFT',
114117
},
115118
ESDTTransferRole,
116119
];

components/operations/operations-checkbox-group.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ type OperationsCheckboxGroupProps = {
1414
name: string;
1515
description: string;
1616
label: string;
17+
disabledItems?: string[];
1718
};
1819

1920
export const OperationsCheckboxGroup = ({
2021
items,
22+
disabledItems,
2123
name,
2224
description,
2325
label,
@@ -39,13 +41,17 @@ export const OperationsCheckboxGroup = ({
3941
key={property.name}
4042
control={control}
4143
name={name}
44+
disabled={
45+
disabledItems ? disabledItems.includes(property.name) : false
46+
}
4247
render={({ field }) => {
4348
return (
4449
<FormItem key={property.name}>
4550
<div className="flex flex-row items-start space-x-3 space-y-0">
4651
<FormControl>
4752
<Checkbox
4853
checked={field.value?.includes(property.name)}
54+
disabled={field.disabled}
4955
onCheckedChange={(checked) => {
5056
return checked
5157
? field.onChange([...field.value, property.name])

components/operations/operations-select-field.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import { useFormContext } from 'react-hook-form';
1010
import {
1111
Select,
1212
SelectContent,
13+
SelectGroup,
1314
SelectItem,
1415
SelectTrigger,
1516
SelectValue,
1617
} from '@/components/ui/select';
17-
import { ScrollArea } from '@/components/ui/scroll-area';
1818

1919
type OperationsSelectFieldProps = {
2020
name: string;
@@ -47,13 +47,13 @@ export const OperationsSelectField = ({
4747
</SelectTrigger>
4848
</FormControl>
4949
<SelectContent>
50-
<ScrollArea className="h-[230px]" type="auto">
50+
<SelectGroup className="overflow-y-auto max-h-[15rem]">
5151
{options.map(({ value, label }) => (
5252
<SelectItem key={value} value={value}>
5353
{label}
5454
</SelectItem>
5555
))}
56-
</ScrollArea>
56+
</SelectGroup>
5757
</SelectContent>
5858
</Select>
5959
<FormDescription className="text-xs">{description}</FormDescription>

components/ui/scroll-area.tsx

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)