Skip to content

Commit fed0f41

Browse files
github-actions[bot]ItsnotakaMarfuen
authored
feat(trust): add loading skeletons for grants and requests tabs (#1768)
Co-authored-by: Daniel Fu <itsnotaka@gmail.com> Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
1 parent 6c6f188 commit fed0f41

File tree

2 files changed

+169
-104
lines changed

2 files changed

+169
-104
lines changed

apps/app/src/app/(app)/[orgId]/trust/components/grants-tab.tsx

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useAccessGrants } from '@/hooks/use-access-requests';
22
import { Badge } from '@comp/ui/badge';
33
import { Button } from '@comp/ui/button';
4+
import { Skeleton } from '@comp/ui/skeleton';
45
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@comp/ui/table';
56
import { useState } from 'react';
67
import { RevokeDialog } from './revoke-dialog';
@@ -9,14 +10,6 @@ export function GrantsTab({ orgId }: { orgId: string }) {
910
const { data, isLoading } = useAccessGrants(orgId);
1011
const [revokeId, setRevokeId] = useState<string | null>(null);
1112

12-
if (isLoading) {
13-
return <div className="p-4 text-muted-foreground">Loading grants...</div>;
14-
}
15-
16-
if (!data || data.length === 0) {
17-
return <div className="p-4 text-muted-foreground">No access grants yet</div>;
18-
}
19-
2013
return (
2114
<div className="space-y-3">
2215
<Table>
@@ -30,35 +23,70 @@ export function GrantsTab({ orgId }: { orgId: string }) {
3023
</TableRow>
3124
</TableHeader>
3225
<TableBody>
33-
{data.map((grant) => (
34-
<TableRow key={grant.id}>
35-
<TableCell>{grant.subjectEmail}</TableCell>
36-
<TableCell>
37-
<Badge
38-
variant={
39-
grant.status === 'active'
40-
? 'default'
41-
: grant.status === 'revoked'
42-
? 'destructive'
43-
: 'secondary'
44-
}
45-
>
46-
{grant.status}
47-
</Badge>
48-
</TableCell>
49-
<TableCell>{new Date(grant.expiresAt).toLocaleDateString()}</TableCell>
50-
<TableCell>
51-
{grant.revokedAt ? new Date(grant.revokedAt).toLocaleDateString() : '-'}
52-
</TableCell>
53-
<TableCell>
54-
{grant.status === 'active' && (
55-
<Button size="sm" variant="destructive" onClick={() => setRevokeId(grant.id)}>
56-
Revoke
57-
</Button>
58-
)}
59-
</TableCell>
60-
</TableRow>
61-
))}
26+
{isLoading
27+
? Array.from({ length: 5 }).map((_, index) => (
28+
<TableRow key={index} className="h-[45px]">
29+
<TableCell className="w-[260px]">
30+
<Skeleton className="h-3.5 w-[80%]" />
31+
</TableCell>
32+
<TableCell className="w-[120px]">
33+
<Skeleton className="h-5 w-[70%]" />
34+
</TableCell>
35+
<TableCell className="w-[160px]">
36+
<Skeleton className="h-3.5 w-[60%]" />
37+
</TableCell>
38+
<TableCell className="w-[160px]">
39+
<Skeleton className="h-3.5 w-[60%]" />
40+
</TableCell>
41+
<TableCell className="w-[140px]">
42+
<Skeleton className="h-3.5 w-[70%]" />
43+
</TableCell>
44+
</TableRow>
45+
))
46+
: data && data.length > 0
47+
? data.map((grant) => (
48+
<TableRow key={grant.id}>
49+
<TableCell>{grant.subjectEmail}</TableCell>
50+
<TableCell>
51+
<Badge
52+
variant={
53+
grant.status === 'active'
54+
? 'default'
55+
: grant.status === 'revoked'
56+
? 'destructive'
57+
: 'secondary'
58+
}
59+
>
60+
{grant.status}
61+
</Badge>
62+
</TableCell>
63+
<TableCell>{new Date(grant.expiresAt).toLocaleDateString()}</TableCell>
64+
<TableCell>
65+
{grant.revokedAt ? new Date(grant.revokedAt).toLocaleDateString() : '-'}
66+
</TableCell>
67+
<TableCell>
68+
{grant.status === 'active' && (
69+
<Button
70+
size="sm"
71+
variant="destructive"
72+
onClick={() => setRevokeId(grant.id)}
73+
>
74+
Revoke
75+
</Button>
76+
)}
77+
</TableCell>
78+
</TableRow>
79+
))
80+
: (
81+
<TableRow>
82+
<TableCell
83+
colSpan={5}
84+
className="py-8 text-center text-sm text-muted-foreground"
85+
>
86+
No access grants yet
87+
</TableCell>
88+
</TableRow>
89+
)}
6290
</TableBody>
6391
</Table>
6492
{revokeId && (

apps/app/src/app/(app)/[orgId]/trust/components/request-tab.tsx

Lines changed: 104 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useAccessRequests, usePreviewNda, useResendNda } from '@/hooks/use-access-requests';
22
import { Badge } from '@comp/ui/badge';
33
import { Button } from '@comp/ui/button';
4+
import { Skeleton } from '@comp/ui/skeleton';
45
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@comp/ui/table';
56
import { useState } from 'react';
67
import { toast } from 'sonner';
@@ -36,14 +37,6 @@ export function RequestsTab({ orgId }: { orgId: string }) {
3637
);
3738
};
3839

39-
if (isLoading) {
40-
return <div className="p-4 text-muted-foreground">Loading requests...</div>;
41-
}
42-
43-
if (!data || data.length === 0) {
44-
return <div className="p-4 text-muted-foreground">No access requests yet</div>;
45-
}
46-
4740
return (
4841
<div className="space-y-3">
4942
<Table>
@@ -61,72 +54,116 @@ export function RequestsTab({ orgId }: { orgId: string }) {
6154
</TableRow>
6255
</TableHeader>
6356
<TableBody>
64-
{data.map((request) => {
65-
const ndaPending = request.status === 'approved' && !request.grant;
66-
return (
67-
<TableRow key={request.id}>
68-
<TableCell>{new Date(request.createdAt).toLocaleDateString()}</TableCell>
69-
<TableCell>{request.name}</TableCell>
70-
<TableCell>{request.email}</TableCell>
71-
<TableCell>{request.company || '-'}</TableCell>
72-
<TableCell className="max-w-xs truncate">{request.purpose || '-'}</TableCell>
73-
<TableCell>{request.requestedDurationDays ?? 30}d</TableCell>
74-
<TableCell>
75-
<Badge
76-
variant={
77-
request.status === 'approved'
78-
? 'default'
79-
: request.status === 'denied'
80-
? 'destructive'
81-
: 'secondary'
82-
}
83-
>
84-
{request.status}
85-
</Badge>
57+
{isLoading ? (
58+
Array.from({ length: 5 }).map((_, index) => (
59+
<TableRow key={index} className="h-[45px]">
60+
<TableCell className="w-[120px]">
61+
<Skeleton className="h-3.5 w-[70%]" />
8662
</TableCell>
87-
<TableCell>
88-
{ndaPending ? (
89-
<Badge variant="warning">pending</Badge>
90-
) : request.grant ? (
91-
<Badge variant="default">signed</Badge>
92-
) : (
93-
'-'
94-
)}
63+
<TableCell className="w-[180px]">
64+
<Skeleton className="h-3.5 w-[80%]" />
9565
</TableCell>
96-
<TableCell>
97-
<div className="flex gap-2">
98-
<Button
99-
size="sm"
100-
disabled={request.status !== 'under_review'}
101-
onClick={() => setApproveId(request.id)}
102-
>
103-
Approve
104-
</Button>
105-
<Button
106-
size="sm"
107-
variant="outline"
108-
disabled={request.status !== 'under_review'}
109-
onClick={() => setDenyId(request.id)}
66+
<TableCell className="w-[220px]">
67+
<Skeleton className="h-3.5 w-[80%]" />
68+
</TableCell>
69+
<TableCell className="w-[160px]">
70+
<Skeleton className="h-3.5 w-[70%]" />
71+
</TableCell>
72+
<TableCell className="max-w-xs">
73+
<Skeleton className="h-3.5 w-[90%]" />
74+
</TableCell>
75+
<TableCell className="w-[80px]">
76+
<Skeleton className="h-3.5 w-[60%]" />
77+
</TableCell>
78+
<TableCell className="w-[110px]">
79+
<Skeleton className="h-5 w-[70%]" />
80+
</TableCell>
81+
<TableCell className="w-[110px]">
82+
<Skeleton className="h-5 w-[70%]" />
83+
</TableCell>
84+
<TableCell className="w-[220px]">
85+
<Skeleton className="h-3.5 w-[80%]" />
86+
</TableCell>
87+
</TableRow>
88+
))
89+
) : data && data.length > 0 ? (
90+
data.map((request) => {
91+
const ndaPending = request.status === 'approved' && !request.grant;
92+
return (
93+
<TableRow key={request.id}>
94+
<TableCell>{new Date(request.createdAt).toLocaleDateString()}</TableCell>
95+
<TableCell>{request.name}</TableCell>
96+
<TableCell>{request.email}</TableCell>
97+
<TableCell>{request.company || '-'}</TableCell>
98+
<TableCell className="max-w-xs truncate">{request.purpose || '-'}</TableCell>
99+
<TableCell>{request.requestedDurationDays ?? 30}d</TableCell>
100+
<TableCell>
101+
<Badge
102+
variant={
103+
request.status === 'approved'
104+
? 'default'
105+
: request.status === 'denied'
106+
? 'destructive'
107+
: 'secondary'
108+
}
110109
>
111-
Deny
112-
</Button>
113-
{ndaPending && (
110+
{request.status}
111+
</Badge>
112+
</TableCell>
113+
<TableCell>
114+
{ndaPending ? (
115+
<Badge variant="warning">pending</Badge>
116+
) : request.grant ? (
117+
<Badge variant="default">signed</Badge>
118+
) : (
119+
'-'
120+
)}
121+
</TableCell>
122+
<TableCell>
123+
<div className="flex gap-2">
114124
<Button
115125
size="sm"
116-
variant="secondary"
117-
onClick={() => handleResendNda(request.id)}
126+
disabled={request.status !== 'under_review'}
127+
onClick={() => setApproveId(request.id)}
118128
>
119-
Resend NDA
129+
Approve
120130
</Button>
121-
)}
122-
<Button size="sm" variant="ghost" onClick={() => handlePreviewNda(request.id)}>
123-
Preview
124-
</Button>
125-
</div>
126-
</TableCell>
127-
</TableRow>
128-
);
129-
})}
131+
<Button
132+
size="sm"
133+
variant="outline"
134+
disabled={request.status !== 'under_review'}
135+
onClick={() => setDenyId(request.id)}
136+
>
137+
Deny
138+
</Button>
139+
{ndaPending && (
140+
<Button
141+
size="sm"
142+
variant="secondary"
143+
onClick={() => handleResendNda(request.id)}
144+
>
145+
Resend NDA
146+
</Button>
147+
)}
148+
<Button
149+
size="sm"
150+
variant="ghost"
151+
onClick={() => handlePreviewNda(request.id)}
152+
>
153+
Preview
154+
</Button>
155+
</div>
156+
</TableCell>
157+
</TableRow>
158+
);
159+
})
160+
) : (
161+
<TableRow>
162+
<TableCell colSpan={9} className="py-8 text-center text-sm text-muted-foreground">
163+
No access requests yet
164+
</TableCell>
165+
</TableRow>
166+
)}
130167
</TableBody>
131168
</Table>
132169
{approveId && (

0 commit comments

Comments
 (0)