1'use client';
2
3import * as React from 'react';
4import { Attachment } from '@/components/ui/attachment';
5
6const uploads = [
7 { name: 'customer-export.csv', type: 'text/csv', size: 12_600_000, speed: 9 },
8 { name: 'onboarding.mp4', type: 'video/mp4', size: 148_000_000, speed: 3 },
9 { name: 'contract-v3.pdf', type: 'application/pdf', size: 820_000, speed: 0, failed: true },
10];
11
12export function Uploading() {
13 const [progress, setProgress] = React.useState(() => uploads.map(() => 0));
14
15 React.useEffect(() => {
16 const timer = setInterval(() => {
17 setProgress((current) =>
18 current.map((value, index) => {
19 const upload = uploads[index];
20 if (!upload || upload.failed) return value;
21 return Math.min(value + upload.speed, 100);
22 }),
23 );
24 }, 300);
25 return () => clearInterval(timer);
26 }, []);
27
28 return (
29 <Attachment.List className="w-full max-w-sm">
30 {uploads.map((upload, index) => {
31 const value = progress[index] ?? 0;
32 const status = upload.failed ? 'error' : value >= 100 ? 'done' : 'uploading';
33
34 return (
35 <Attachment.Item key={upload.name}>
36 <Attachment status={status}>
37 <Attachment.Preview type={upload.type} name={upload.name} />
38 <Attachment.Content>
39 <Attachment.Name>{upload.name}</Attachment.Name>
40 <Attachment.Meta>
41 {status === 'error' && (
42 <span className="text-destructive">Upload failed, try again</span>
43 )}
44 {status === 'uploading' && (
45 <>
46 {Math.round(value)}% of <Attachment.Size bytes={upload.size} />
47 </>
48 )}
49 {status === 'done' && <Attachment.Size bytes={upload.size} />}
50 </Attachment.Meta>
51 </Attachment.Content>
52 <Attachment.Actions>
53 <Attachment.Remove
54 aria-label={status === 'uploading' ? 'Cancel upload' : 'Remove'}
55 />
56 </Attachment.Actions>
57 {status === 'uploading' && <Attachment.Progress value={value} />}
58 </Attachment>
59 </Attachment.Item>
60 );
61 })}
62 </Attachment.List>
63 );
64}