Skip to content

Commit 3d4f028

Browse files
authored
fix(tangle-cloud): UI/UX Fixes & Improvements (#3036)
1 parent 2715ea7 commit 3d4f028

File tree

19 files changed

+420
-151
lines changed

19 files changed

+420
-151
lines changed

apps/tangle-cloud/src/components/TangleCloudCard.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ const TangleCloudCard: FC<TangleCloudCardProps> = ({ children, className }) => {
99
<Card
1010
variant={CardVariant.GLASS}
1111
className={twMerge(
12-
'relative overflow-hidden shadow-sm',
13-
'w-full xl:w-1/2 p-5 z-0',
12+
'relative overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300',
13+
'w-full xl:w-1/2 p-6 z-0',
14+
'border border-mono-40 dark:border-mono-160',
15+
'backdrop-blur-sm',
1416
className,
1517
)}
1618
>
@@ -21,10 +23,12 @@ const TangleCloudCard: FC<TangleCloudCardProps> = ({ children, className }) => {
2123
'absolute top-0 left-0 w-full h-full z-0',
2224
"dark:bg-[url('/static/assets/accounts/grid-bg-colored.svg')]",
2325
'bg-cover bg-center bg-no-repeat',
24-
'bg-gradient-to-b from-mono-0.7 to-transparent',
25-
'shadow-[0px_4px_8px_0px_rgba(0,0,0,0.08)]',
26-
'dark:shadow-[0px_4px_8px_0px_rgba(0,0,0,0.20)]',
27-
'dark:bg-blend-lighten',
26+
'bg-gradient-to-br from-blue-5 via-mono-10 to-purple-5',
27+
'dark:bg-gradient-to-br dark:from-blue-180 dark:via-mono-180 dark:to-purple-180',
28+
'opacity-60 dark:opacity-40',
29+
'shadow-[0px_8px_24px_0px_rgba(0,0,0,0.12)]',
30+
'dark:shadow-[0px_8px_24px_0px_rgba(0,0,0,0.40)]',
31+
'dark:bg-blend-overlay',
2832
)}
2933
/>
3034
</Card>

apps/tangle-cloud/src/components/accountStatsCard/AccountStatsCardBody.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Children, type FC } from 'react';
1+
import { type FC } from 'react';
22
import { Socials, StatsItem } from '@tangle-network/ui-components';
33
import { AccountStatsCardBodyProps } from '.';
44
import cx from 'classnames';
@@ -12,22 +12,36 @@ export const AccountStatsCardBody: FC<AccountStatsCardBodyProps> = ({
1212
}) => {
1313
return (
1414
<div {...props} className="w-full space-y-5">
15-
<div className="grid grid-cols-2">
16-
{Children.toArray(
17-
statsItems.map((item, index) => (
15+
<div className="grid grid-cols-2 grid-rows-2 min-h-[120px]">
16+
{Array.from({ length: 4 }).map((_, index) => {
17+
const item = statsItems[index];
18+
const isLeftColumn = index % 2 === 0;
19+
const isTopRow = index < 2;
20+
21+
return item ? (
1822
<StatsItem
23+
key={index}
1924
className={cx('gap-0 border-mono-100 dark:border-mono-140 p-2', {
20-
'border-r': index % 2 === 0,
21-
'border-b': index < statsItems.length - 2,
22-
'pl-5': index % 2 === 1,
25+
'border-r': isLeftColumn,
26+
'border-b': isTopRow,
27+
'pl-5': !isLeftColumn,
2328
})}
2429
title={item.title}
2530
tooltip={item.tooltip}
2631
>
2732
{item.children}
2833
</StatsItem>
29-
)),
30-
)}
34+
) : (
35+
<div
36+
key={`placeholder-${index}`}
37+
className={cx('border-mono-100 dark:border-mono-140 p-2', {
38+
'border-r': isLeftColumn,
39+
'border-b': isTopRow,
40+
'pl-5': !isLeftColumn,
41+
})}
42+
/>
43+
);
44+
})}
3145
</div>
3246

3347
<Socials

apps/tangle-cloud/src/data/operators/useOperatorStatsData.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const operatorStatsSchema = z.object({
2929

3030
export const useOperatorStatsData = (
3131
operatorAddress: SubstrateAddress | null | undefined,
32+
refreshTrigger?: number,
3233
) => {
3334
const { network } = useNetworkStore();
3435

@@ -162,7 +163,6 @@ export const useOperatorStatsData = (
162163
}),
163164
);
164165

165-
// TODO: after the instance is terminated, this will be removed. using Graphql to get the deployed services
166166
const deployedServices$ =
167167
apiRx.query.services?.instances === undefined
168168
? of({})
@@ -213,7 +213,8 @@ export const useOperatorStatsData = (
213213
),
214214
);
215215
},
216-
[operatorAddress, network.ss58Prefix],
216+
// eslint-disable-next-line react-hooks/exhaustive-deps
217+
[operatorAddress, network.ss58Prefix, refreshTrigger],
217218
),
218219
);
219220

apps/tangle-cloud/src/data/operators/useUserStatsData.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ const userStatsSchema = z.object({
1616
consumedServices: z.number().default(0),
1717
});
1818

19-
export const useUserStatsData = (accountAddress: string | null | undefined) => {
19+
export const useUserStatsData = (
20+
accountAddress: string | null | undefined,
21+
refreshTrigger?: number,
22+
) => {
2023
const { result: userStats, ...rest } = useApiRx(
2124
useCallback(
2225
(apiRx) => {
@@ -221,7 +224,8 @@ export const useUserStatsData = (accountAddress: string | null | undefined) => {
221224
}),
222225
);
223226
},
224-
[accountAddress],
227+
// eslint-disable-next-line react-hooks/exhaustive-deps
228+
[accountAddress, refreshTrigger],
225229
),
226230
);
227231

apps/tangle-cloud/src/data/services/useUserOwnedInstances.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';
1616

1717
export const useUserOwnedInstances = (
1818
userAddress: SubstrateAddress | null | undefined,
19+
refreshTrigger?: number,
1920
) => {
2021
const { result: userOwnedData, ...rest } = useApiRx(
2122
useCallback(
@@ -163,7 +164,8 @@ export const useUserOwnedInstances = (
163164
}),
164165
);
165166
},
166-
[userAddress],
167+
// eslint-disable-next-line react-hooks/exhaustive-deps
168+
[userAddress, refreshTrigger],
167169
),
168170
);
169171

apps/tangle-cloud/src/pages/blueprints/[id]/page.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ const Page = () => {
6262
<div className="space-y-5">
6363
<SkeletonLoader className="min-h-64" />
6464

65-
<Typography variant="h4" fw="bold">
66-
Operators running
67-
</Typography>
68-
6965
<SkeletonLoader className="min-h-52" />
7066
</div>
7167
);
@@ -111,13 +107,16 @@ const Page = () => {
111107
/>
112108

113109
<div className="space-y-5">
114-
<Typography variant="h4" fw="bold">
115-
Registered Operators
116-
</Typography>
110+
{!isLoading && (
111+
<Typography variant="h4" fw="bold">
112+
Registered Operators
113+
</Typography>
114+
)}
117115

118116
<OperatorsTable
119117
RestakeOperatorAction={RestakeOperatorAction}
120118
data={result.operators}
119+
isLoading={isLoading}
121120
/>
122121
</div>
123122

apps/tangle-cloud/src/pages/instances/AccountStatsCard.tsx

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
isSubstrateAddress,
88
KeyValueWithButton,
99
shortenString,
10+
Chip,
1011
} from '@tangle-network/ui-components';
1112
import useNetworkStore from '@tangle-network/tangle-shared-ui/context/useNetworkStore';
1213
import useSWRImmutable from 'swr/immutable';
@@ -21,7 +22,10 @@ import useOperatorInfo from '@tangle-network/tangle-shared-ui/hooks/useOperatorI
2122
import { useOperatorStatsData } from '../../data/operators/useOperatorStatsData';
2223
import { useUserStatsData } from '../../data/operators/useUserStatsData';
2324

24-
export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
25+
export const AccountStatsCard: FC<
26+
AccountStatsCardProps & { refreshTrigger?: number }
27+
> = (props) => {
28+
const { refreshTrigger, ...cardProps } = props;
2529
const accountAddress = useActiveAccountAddress();
2630
const { isOperator } = useOperatorInfo();
2731
const rpcEndpoints = useNetworkStore((store) => store.network.wsRpcEndpoints);
@@ -39,6 +43,7 @@ export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
3943

4044
return accountAddress;
4145
}, [accountAddress, isOperator]),
46+
refreshTrigger,
4247
);
4348

4449
const { result: userStatsData } = useUserStatsData(
@@ -49,6 +54,7 @@ export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
4954

5055
return accountAddress;
5156
}, [accountAddress]),
57+
refreshTrigger,
5258
);
5359

5460
const { data: accountInfo } = useSWRImmutable(
@@ -133,40 +139,41 @@ export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
133139
operatorStatsData && operatorStatsData.registeredBlueprints > 0;
134140
const isActiveOperator = isOperator && hasOperatorData;
135141

136-
return [
137-
{
138-
title: 'Registered Blueprints',
139-
children: isActiveOperator ? operatorStatsData.registeredBlueprints : 0,
140-
tooltip: 'Number of blueprints you have registered as an operator',
141-
},
142-
{
143-
title: 'Running Services',
144-
children: isActiveOperator
145-
? operatorStatsData.runningServices
146-
: userStatsData.runningServices,
147-
tooltip: isActiveOperator
148-
? 'Services currently running that you operate as an operator'
149-
: 'Services currently running that you have deployed',
150-
},
151-
{
152-
title: 'Pending Services',
153-
children: isActiveOperator
154-
? operatorStatsData.pendingServices
155-
: userStatsData.pendingServices,
156-
tooltip: isActiveOperator
157-
? 'Service requests pending your approval/rejection as an operator'
158-
: 'Service requests you have submitted that are pending operator approval',
159-
},
160-
{
161-
title: 'Deployed Services',
162-
children: userStatsData.deployedServices,
163-
tooltip: 'Total services you have deployed as a user/deployer',
164-
},
165-
];
166-
}, [operatorStatsData, userStatsData, isOperator]);
142+
const items = [];
143+
144+
// Only show operator-specific stats if user is an operator with registered blueprints
145+
if (isActiveOperator) {
146+
items.push(
147+
{
148+
title: 'Registered Blueprints',
149+
children: operatorStatsData.registeredBlueprints,
150+
tooltip: 'Number of blueprints you have registered as an operator',
151+
},
152+
{
153+
title: 'Running Services',
154+
children: operatorStatsData.runningServices,
155+
tooltip: 'Services currently running that you operate as an operator',
156+
},
157+
{
158+
title: 'Pending Services',
159+
children: operatorStatsData.pendingServices,
160+
tooltip:
161+
'Service requests pending your approval/rejection as an operator',
162+
},
163+
);
164+
}
165+
166+
// Always show deployed services for all users
167+
items.push({
168+
title: 'Deployed Services',
169+
children: userStatsData.deployedServices,
170+
tooltip: 'Total services you have deployed as a user/deployer',
171+
});
167172

173+
return items;
174+
}, [operatorStatsData, userStatsData, isOperator]);
168175
return (
169-
<AccountStatsDetailCard.Root {...props.rootProps}>
176+
<AccountStatsDetailCard.Root {...cardProps.rootProps}>
170177
<AccountStatsDetailCard.Header
171178
IconElement={
172179
<Avatar
@@ -176,6 +183,13 @@ export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
176183
/>
177184
}
178185
title={identityName}
186+
RightElement={
187+
isOperator ? (
188+
<Chip color="blue" className="text-xs px-2 py-1">
189+
Operator
190+
</Chip>
191+
) : undefined
192+
}
179193
description={
180194
<KeyValueWithButton
181195
size="sm"
Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,50 @@
11
import { RegisteredBlueprintsTabs } from './RegisteredBlueprints';
22
import { InstancesTabs } from './Instances';
3-
import { FC } from 'react';
3+
import { FC, Dispatch, SetStateAction, useMemo } from 'react';
44
import useOperatorInfo from '@tangle-network/tangle-shared-ui/hooks/useOperatorInfo';
5+
import { useOperatorStatsData } from '../../data/operators/useOperatorStatsData';
6+
import useActiveAccountAddress from '@tangle-network/tangle-shared-ui/hooks/useActiveAccountAddress';
7+
import { isSubstrateAddress } from '@tangle-network/ui-components';
58

6-
export const BlueprintManagementSection: FC = () => {
9+
interface BlueprintManagementSectionProps {
10+
refreshTrigger: number;
11+
setRefreshTrigger: Dispatch<SetStateAction<number>>;
12+
}
13+
14+
export const BlueprintManagementSection: FC<
15+
BlueprintManagementSectionProps
16+
> = ({ refreshTrigger, setRefreshTrigger }) => {
717
const { isOperator } = useOperatorInfo();
18+
const accountAddress = useActiveAccountAddress();
19+
20+
const { result: operatorStatsData } = useOperatorStatsData(
21+
useMemo(() => {
22+
if (
23+
!accountAddress ||
24+
!isOperator ||
25+
!isSubstrateAddress(accountAddress)
26+
) {
27+
return null;
28+
}
29+
30+
return accountAddress;
31+
}, [accountAddress, isOperator]),
32+
refreshTrigger,
33+
);
34+
35+
const hasRegisteredBlueprints = useMemo(() => {
36+
return operatorStatsData && operatorStatsData.registeredBlueprints > 0;
37+
}, [operatorStatsData]);
38+
39+
const shouldShowRegisteredBlueprints = isOperator && hasRegisteredBlueprints;
840

941
return (
1042
<>
11-
{isOperator && <RegisteredBlueprintsTabs />}
12-
<InstancesTabs />
43+
{shouldShowRegisteredBlueprints && <RegisteredBlueprintsTabs />}
44+
<InstancesTabs
45+
refreshTrigger={refreshTrigger}
46+
setRefreshTrigger={setRefreshTrigger}
47+
/>
1348
</>
1449
);
1550
};

0 commit comments

Comments
 (0)