Skip to content

Commit a32c0a7

Browse files
bayulaksana479andrewklau
authored andcommitted
feat(app): account contract tab
1 parent e0dc722 commit a32c0a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+4043
-691
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dist
33
out
44
static
55
.next
6+
.next-docs/
67
.bos
78
*.css
89
__ENV.js

apps/api/src/middlewares/rateLimiter.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RateLimiterRedis, RateLimiterUnion } from 'rate-limiter-flexible';
55
import config from '#config';
66
import catchAsync from '#libs/async';
77
import dayjs from '#libs/dayjs';
8-
import logger from '#libs/logger';
8+
// import logger from '#libs/logger';
99
import { userSql } from '#libs/postgres';
1010
import { ratelimiterRedisClient } from '#libs/ratelimiterRedis';
1111
import { SubscriptionStatus } from '#types/enums';
@@ -103,7 +103,7 @@ const rateLimiter = catchAsync(
103103
);
104104
}
105105
} catch (error) {
106-
logger.error(error);
106+
// logger.error(error);
107107

108108
return await useFreePlan(req, res, next, req.ip!);
109109
}
@@ -142,7 +142,7 @@ const rateLimiter = catchAsync(
142142
await rateLimit.consume(tokenKey, consumeCount);
143143
}
144144
} catch (error) {
145-
logger.error(error);
145+
// logger.error(error);
146146
}
147147

148148
try {
@@ -193,7 +193,7 @@ const useFreePlan = async (
193193
86400,
194194
);
195195
} catch (redisError) {
196-
logger.error(redisError, 'Error caching user plan in Redis.');
196+
// logger.error(redisError, 'Error caching user plan in Redis.');
197197
}
198198
const { limiters, rateLimit } = rateLimiterUnion(plan, baseUrl);
199199

@@ -213,7 +213,7 @@ const useFreePlan = async (
213213
await rateLimit.consume(tokenKey, consumeCount);
214214
}
215215
} catch (error) {
216-
logger.error(error);
216+
// logger.error(error);
217217
}
218218

219219
try {
@@ -254,7 +254,7 @@ const getFreePlan = async () => {
254254
if (cachedFreePlan) {
255255
return JSON.parse(cachedFreePlan);
256256
}
257-
logger.info('Free plan not found in Redis cache, fetching from DB.');
257+
// logger.info('Free plan not found in Redis cache, fetching from DB.');
258258

259259
const plans = await userSql<Plan[]>`
260260
SELECT
@@ -276,19 +276,19 @@ const getFreePlan = async () => {
276276
86400,
277277
);
278278
} catch (redisError) {
279-
logger.error(redisError, 'Error caching free plan in Redis.');
279+
// logger.error(redisError, 'Error caching free plan in Redis.');
280280
}
281281

282282
return freePlan;
283283
}
284-
logger.warn('No valid free plan found, returning null.');
284+
// logger.warn('No valid free plan found, returning null.');
285285

286286
return null;
287287
} catch (error) {
288-
logger.error(
289-
error,
290-
'Free plan not available, applying default rate limit.',
291-
);
288+
// logger.error(
289+
// error,
290+
// 'Free plan not available, applying default rate limit.',
291+
// );
292292

293293
return null;
294294
}

apps/frontend/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ yarn-error.log*
3939
# typescript
4040
*.tsbuildinfo
4141
next-env.d.ts
42+
# next-agents-md
43+
.next-docs/

apps/frontend/package.json

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,60 +10,51 @@
1010
"codemod": "jscodeshift -t codemod.js --parser tsx --extensions tsx src/ui/*.tsx"
1111
},
1212
"dependencies": {
13+
"@base-ui/react": "~1.1.0",
1314
"@highcharts/react": "~4.1.0",
1415
"@hot-labs/near-connect": "~0.8.2",
1516
"@near-js/jsonrpc-client": "~1.6.0",
16-
"@radix-ui/react-accordion": "~1.2.12",
17-
"@radix-ui/react-checkbox": "~1.3.3",
18-
"@radix-ui/react-collapsible": "~1.1.12",
19-
"@radix-ui/react-dialog": "~1.1.15",
20-
"@radix-ui/react-dropdown-menu": "~2.1.16",
21-
"@radix-ui/react-label": "~2.1.8",
22-
"@radix-ui/react-navigation-menu": "~1.2.14",
23-
"@radix-ui/react-popover": "~1.1.15",
24-
"@radix-ui/react-scroll-area": "~1.2.10",
25-
"@radix-ui/react-select": "~2.2.6",
26-
"@radix-ui/react-separator": "~1.1.8",
27-
"@radix-ui/react-slot": "~1.2.4",
28-
"@radix-ui/react-switch": "~1.2.6",
29-
"@radix-ui/react-tabs": "~1.1.13",
30-
"@radix-ui/react-tooltip": "~1.2.8",
31-
"@walletconnect/sign-client": "~2.23.2",
17+
"@walletconnect/sign-client": "~2.23.4",
3218
"ahooks": "~3.9.6",
3319
"big.js": "~7.0.1",
3420
"class-variance-authority": "~0.7.1",
3521
"clsx": "~2.1.1",
3622
"cmdk": "~1.1.1",
37-
"dayjs": "1.11.19",
38-
"es-toolkit": "~1.43.0",
23+
"dayjs": "~1.11.19",
24+
"es-toolkit": "~1.44.0",
3925
"highcharts": "~12.5.0",
40-
"next": "~16.1.1",
41-
"react": "~19.2.3",
42-
"react-dom": "~19.2.3",
43-
"react-error-boundary": "~6.0.3",
26+
"near-connect-hooks": "~1.0.3",
27+
"next": "~16.1.6",
28+
"prismjs": "~1.30.0",
29+
"radix-ui": "~1.4.3",
30+
"react": "~19.2.4",
31+
"react-dom": "~19.2.4",
32+
"react-error-boundary": "~6.1.0",
33+
"react-hook-form": "~7.71.1",
4434
"react-icons": "~5.5.0",
45-
"react-timeago": "~8.3.0",
4635
"rosetta": "~1.1.0",
4736
"sonner": "~2.0.7",
4837
"tailwind-merge": "~3.4.0",
4938
"uqr": "~0.1.2",
50-
"zustand": "~5.0.10",
51-
"zod": "~4.3.5"
39+
"zod": "~4.3.6",
40+
"zod-resolver-lite": "~1.0.0",
41+
"zustand": "~5.0.11"
5242
},
5343
"devDependencies": {
5444
"@tailwindcss/postcss": "~4",
55-
"@types/big.js": "~6.2.2",
45+
"@types/big.js": "~6",
5646
"@types/node": "~20",
47+
"@types/prismjs": "~1.26.5",
5748
"@types/react": "~19",
5849
"@types/react-dom": "~19",
5950
"eslint-config-custom-next": "*",
60-
"jscodeshift": "~17.3.0",
51+
"jscodeshift": "~17",
6152
"nb-schemas": "*",
6253
"nb-tsconfig": "*",
6354
"nb-types": "*",
64-
"prettier-plugin-tailwindcss": "~0.7.2",
55+
"prettier-plugin-tailwindcss": "~0",
6556
"tailwindcss": "~4",
66-
"tw-animate-css": "~1.4.0",
57+
"tw-animate-css": "~1",
6758
"typescript": "~5"
6859
}
6960
}

apps/frontend/src/app/[lang]/address/[address]/analytics/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const AnalyticsLayout = async ({ children, params }: Props) => {
1212
return (
1313
<Card>
1414
<CardContent className="text-body-sm p-3">
15-
<ScrollArea className="mb-2 w-full whitespace-nowrap">
15+
<ScrollArea className="mb-3 w-full whitespace-nowrap">
1616
<TabLinks>
1717
<TabLink asChild>
1818
<ActiveLink href={`/address/${address}/analytics`}>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { redirect } from 'next/navigation';
2+
3+
import { ContractCode } from '@/components/address/contract/code';
4+
import { fetchContract } from '@/data/address/contract';
5+
6+
type Props = PageProps<'/[lang]/address/[address]/contract/code'>;
7+
8+
const CodePage = async ({ params }: Props) => {
9+
const { address } = await params;
10+
const contract = await fetchContract(address);
11+
12+
if (!contract) return redirect(`/address/${address}`);
13+
14+
return <ContractCode contract={contract} />;
15+
};
16+
17+
export default CodePage;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { ActiveLink } from '@/components/link';
2+
import { TabLink } from '@/components/tab-links';
3+
import { TabLinks } from '@/components/tab-links';
4+
import { Card, CardContent } from '@/ui/card';
5+
import { ScrollArea, ScrollBar } from '@/ui/scroll-area';
6+
7+
type Props = LayoutProps<'/[lang]/address/[address]/contract'>;
8+
9+
const ContractLayout = async ({ children, params }: Props) => {
10+
const { address } = await params;
11+
12+
return (
13+
<Card>
14+
<CardContent className="text-body-sm p-3">
15+
<ScrollArea className="mb-3 w-full whitespace-nowrap">
16+
<TabLinks>
17+
<TabLink asChild>
18+
<ActiveLink href={`/address/${address}/contract`}>
19+
Overview
20+
</ActiveLink>
21+
</TabLink>
22+
<TabLink asChild>
23+
<ActiveLink href={`/address/${address}/contract/code`}>
24+
Contract Code
25+
</ActiveLink>
26+
</TabLink>
27+
<TabLink asChild>
28+
<ActiveLink href={`/address/${address}/contract/methods`}>
29+
Contract Methods
30+
</ActiveLink>
31+
</TabLink>
32+
</TabLinks>
33+
<ScrollBar orientation="horizontal" />
34+
</ScrollArea>
35+
{children}
36+
</CardContent>
37+
</Card>
38+
);
39+
};
40+
41+
export default ContractLayout;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { redirect } from 'next/navigation';
2+
3+
import { ContractMethods } from '@/components/address/contract/methods';
4+
import { ErrorSuspense } from '@/components/error-suspense';
5+
import { fetchContract, fetchSchema } from '@/data/address/contract';
6+
7+
type Props = PageProps<'/[lang]/address/[address]/contract/methods'>;
8+
9+
const MethodsPage = async ({ params }: Props) => {
10+
const { address } = await params;
11+
const contract = await fetchContract(address);
12+
const schemaPromise = fetchSchema(address);
13+
14+
if (!contract) return redirect(`/address/${address}`);
15+
16+
return (
17+
<ErrorSuspense fallback={<ContractMethods loading />}>
18+
<ContractMethods methodsPromise={schemaPromise} />
19+
</ErrorSuspense>
20+
);
21+
};
22+
23+
export default MethodsPage;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { redirect } from 'next/navigation';
2+
3+
import { Contract } from '@/components/address/contract';
4+
import { ErrorSuspense } from '@/components/error-suspense';
5+
import { fetchAccount } from '@/data/address';
6+
import { fetchContract, fetchDeployments } from '@/data/address/contract';
7+
8+
type Props = PageProps<'/[lang]/address/[address]/contract'>;
9+
10+
const ContractPage = async ({ params }: Props) => {
11+
const { address } = await params;
12+
const contract = await fetchContract(address);
13+
const accountPromise = fetchAccount(address);
14+
const deploymentsPromise = fetchDeployments(address);
15+
16+
if (!contract) return redirect(`/address/${address}`);
17+
18+
return (
19+
<ErrorSuspense fallback={<Contract loading />}>
20+
<Contract
21+
accountPromise={accountPromise}
22+
contract={contract}
23+
deploymentsPromise={deploymentsPromise}
24+
/>
25+
</ErrorSuspense>
26+
);
27+
};
28+
29+
export default ContractPage;

apps/frontend/src/app/[lang]/address/[address]/layout.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { notFound } from 'next/navigation';
22

3+
import { ContractTab } from '@/components/address/contract/tab';
34
import { Info } from '@/components/address/info';
45
import { Overview } from '@/components/address/overview';
56
import { AccountQr } from '@/components/address/qr';
@@ -13,14 +14,15 @@ import { TabLinks } from '@/components/tab-links';
1314
import {
1415
fetchAccount,
1516
fetchBalance,
16-
fetchDeployments,
1717
fetchTokenCache,
1818
fetchTokens,
1919
} from '@/data/address';
20+
import { fetchContract, fetchDeployments } from '@/data/address/contract';
2021
import { fetchStats } from '@/data/layout';
2122
import { getDictionary, hasLocale } from '@/locales/dictionaries';
2223
import { LocaleProvider } from '@/providers/locale';
2324
import { ScrollArea, ScrollBar } from '@/ui/scroll-area';
25+
import { Skeleton } from '@/ui/skeleton';
2426

2527
type Props = LayoutProps<'/[lang]/address/[address]'>;
2628

@@ -29,6 +31,7 @@ const AddressLayout = async ({ children, params }: Props) => {
2931
const statsPromise = fetchStats();
3032
const accountPromise = fetchAccount(address);
3133
const balancePromise = fetchBalance(address);
34+
const contractPromise = fetchContract(address);
3235
const deploymentsPromise = fetchDeployments(address);
3336
const tokensPromise = fetchTokens(address);
3437
const tokenCachePromise = fetchTokenCache(address);
@@ -71,7 +74,7 @@ const AddressLayout = async ({ children, params }: Props) => {
7174
/>
7275
</ErrorSuspense>
7376
</div>
74-
<ScrollArea className="mt-10 mb-2 w-full whitespace-nowrap">
77+
<ScrollArea className="mt-10 mb-3 w-full whitespace-nowrap">
7578
<TabLinks>
7679
<TabLink asChild>
7780
<ActiveLink href={`/address/${address}`}>
@@ -84,17 +87,17 @@ const AddressLayout = async ({ children, params }: Props) => {
8487
</ActiveLink>
8588
</TabLink>
8689
<TabLink asChild>
87-
<ActiveLink href={`/address/${address}/fts`}>
90+
<ActiveLink href={`/address/${address}/tokens`}>
8891
Token Txns
8992
</ActiveLink>
9093
</TabLink>
9194
<TabLink asChild>
92-
<ActiveLink href={`/address/${address}/nfts`}>
95+
<ActiveLink href={`/address/${address}/nft-tokens`}>
9396
NFT Txns
9497
</ActiveLink>
9598
</TabLink>
9699
<TabLink asChild>
97-
<ActiveLink href={`/address/${address}/mts`}>
100+
<ActiveLink href={`/address/${address}/mt-tokens`}>
98101
Multi Token Txns
99102
<TabBadge variant="teal">NEW</TabBadge>
100103
</ActiveLink>
@@ -112,7 +115,10 @@ const AddressLayout = async ({ children, params }: Props) => {
112115
</ActiveLink>
113116
</TabLink> */}
114117
<TabLink asChild>
115-
<ActiveLink href={`/address/${address}/analytics`}>
118+
<ActiveLink
119+
exact={false}
120+
href={`/address/${address}/analytics`}
121+
>
116122
Analytics
117123
<TabBadge variant="teal">NEW</TabBadge>
118124
</ActiveLink>
@@ -122,11 +128,12 @@ const AddressLayout = async ({ children, params }: Props) => {
122128
Access Keys
123129
</ActiveLink>
124130
</TabLink>
125-
<TabLink asChild>
126-
<ActiveLink href={`/address/${address}/contract`}>
127-
Contract
128-
</ActiveLink>
129-
</TabLink>
131+
<ErrorSuspense fallback={<Skeleton className="w-20" />}>
132+
<ContractTab
133+
address={address}
134+
contractPromise={contractPromise}
135+
/>
136+
</ErrorSuspense>
130137
</TabLinks>
131138
<ScrollBar orientation="horizontal" />
132139
</ScrollArea>

0 commit comments

Comments
 (0)