Skip to content

Commit 9217165

Browse files
RajVishhRaj Vishwakarma
andauthored
task-export-button: Add task export button with JSON/TXT prompt. (#138)
* task-export-button: Add task export button with JSON/TXT prompt. * CI error: Fix CI errors * Fix CI: fix CI errors --------- Co-authored-by: Raj Vishwakarma <rajvishidk@gmai.com>
1 parent 4826b1c commit 9217165

10 files changed

Lines changed: 340 additions & 116 deletions

File tree

Lines changed: 108 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { Github, LogOut, Trash2 } from 'lucide-react';
1+
import {
2+
Github,
3+
LogOut,
4+
Trash2,
5+
FileDown,
6+
FileText,
7+
FileJson,
8+
} from 'lucide-react';
29
import {
310
DropdownMenu,
411
DropdownMenuContent,
@@ -16,59 +23,112 @@ import {
1623
RouteProps,
1724
Props,
1825
} from './navbar-utils';
26+
import {
27+
Dialog,
28+
DialogContent,
29+
DialogDescription,
30+
DialogHeader,
31+
DialogTrigger,
32+
} from '@/components/ui/dialog';
33+
import { Button } from '@/components/ui/button';
1934
import { url } from '@/components/utils/URLs';
35+
import { exportTasksAsJSON, exportTasksAsTXT } from '@/exports-tasks';
36+
import { useState } from 'react';
2037

2138
export const NavbarDesktop = (
2239
props: Props & {
2340
isLoading: boolean;
2441
setIsLoading: (val: boolean) => void;
2542
}
26-
) => (
27-
<div className="hidden md:flex items-center justify-between w-full">
28-
<nav className="hidden md:flex gap-2 justify-center flex-1">
29-
{routeList.map((route: RouteProps, i) => (
30-
<a
31-
rel="noreferrer noopener"
32-
href={route.href}
33-
key={i}
34-
className={`text-[17px] ${buttonVariants({
35-
variant: 'ghost',
36-
})}`}
37-
>
38-
{route.label}
39-
</a>
40-
))}
41-
</nav>
42-
<div className="hidden md:flex items-center gap-2">
43-
<DropdownMenu>
44-
<DropdownMenuTrigger asChild>
45-
<Avatar>
46-
<AvatarImage src={props.imgurl} />
47-
<AvatarFallback>CN</AvatarFallback>
48-
</Avatar>
49-
</DropdownMenuTrigger>
50-
<DropdownMenuContent className="w-56">
51-
<DropdownMenuLabel>{props.email}</DropdownMenuLabel>
52-
<DropdownMenuItem
53-
className="text-red-500"
54-
onClick={() => deleteAllTasks(props)}
43+
) => {
44+
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
45+
46+
const handleExportJSON = () => {
47+
exportTasksAsJSON(props.tasks || []);
48+
setIsExportDialogOpen(false);
49+
};
50+
51+
const handleExportTXT = () => {
52+
exportTasksAsTXT(props.tasks || []);
53+
setIsExportDialogOpen(false);
54+
};
55+
56+
return (
57+
<Dialog open={isExportDialogOpen} onOpenChange={setIsExportDialogOpen}>
58+
<div className="hidden md:flex items-center justify-between w-full">
59+
<nav className="hidden md:flex gap-2 justify-center flex-1">
60+
{routeList.map((route: RouteProps, i) => (
61+
<a
62+
rel="noreferrer noopener"
63+
href={route.href}
64+
key={i}
65+
className={`text-[17px] ${buttonVariants({ variant: 'ghost' })}`}
66+
>
67+
{route.label}
68+
</a>
69+
))}
70+
</nav>
71+
<div className="hidden md:flex items-center gap-2">
72+
<DropdownMenu>
73+
<DropdownMenuTrigger asChild>
74+
<Avatar>
75+
<AvatarImage src={props.imgurl} />
76+
<AvatarFallback>CN</AvatarFallback>
77+
</Avatar>
78+
</DropdownMenuTrigger>
79+
<DropdownMenuContent className="w-56">
80+
<DropdownMenuLabel>{props.email}</DropdownMenuLabel>
81+
<DropdownMenuItem
82+
className="text-red-500"
83+
onClick={() => deleteAllTasks(props)}
84+
>
85+
<Trash2 className="mr-2 h-4 w-4" />
86+
Delete all tasks
87+
</DropdownMenuItem>
88+
<DropdownMenuItem
89+
onClick={() => window.open(url.githubRepoURL, '_blank')}
90+
>
91+
<Github className="mr-2 h-4 w-4" />
92+
<span>GitHub</span>
93+
</DropdownMenuItem>
94+
<DialogTrigger>
95+
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
96+
<FileDown className="mr-2 h-4 w-4" />
97+
<span>Export tasks</span>
98+
</DropdownMenuItem>
99+
</DialogTrigger>
100+
<DropdownMenuItem onClick={handleLogout}>
101+
<LogOut className="mr-2 h-4 w-4" />
102+
<span>Log out</span>
103+
</DropdownMenuItem>
104+
</DropdownMenuContent>
105+
</DropdownMenu>
106+
<ModeToggle />
107+
</div>
108+
</div>
109+
<DialogContent>
110+
<DialogHeader>
111+
<DialogDescription>
112+
Would you like to download your tasks as a JSON file or a TXT file?
113+
</DialogDescription>
114+
</DialogHeader>
115+
<div className="flex justify-end gap-4 mt-4">
116+
<Button
117+
className="bg-[#3B82F6] hover:bg-white focus-visible:ring-0 focus-visible:ring-offset-0 focus:outline-none"
118+
onClick={handleExportTXT}
55119
>
56-
<Trash2 className="mr-2 h-4 w-4" />
57-
Delete all tasks
58-
</DropdownMenuItem>
59-
<DropdownMenuItem
60-
onClick={() => window.open(url.githubRepoURL, '_blank')}
120+
<FileText className="mr-2 h-4 w-4" />
121+
Download .txt
122+
</Button>
123+
<Button
124+
className="bg-[#3B82F6] hover:bg-white"
125+
onClick={handleExportJSON}
61126
>
62-
<Github className="mr-2 h-4 w-4" />
63-
<span>GitHub</span>
64-
</DropdownMenuItem>
65-
<DropdownMenuItem onClick={handleLogout}>
66-
<LogOut className="mr-2 h-4 w-4" />
67-
<div onClick={handleLogout}>Log out</div>
68-
</DropdownMenuItem>
69-
</DropdownMenuContent>
70-
</DropdownMenu>
71-
<ModeToggle />
72-
</div>
73-
</div>
74-
);
127+
<FileJson className="mr-2 h-4 w-4" />
128+
Download .json
129+
</Button>
130+
</div>
131+
</DialogContent>
132+
</Dialog>
133+
);
134+
};
Lines changed: 136 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from 'react';
12
import {
23
Sheet,
34
SheetContent,
@@ -6,8 +7,15 @@ import {
67
SheetTrigger,
78
} from '@/components/ui/sheet';
89
import { url } from '@/components/utils/URLs';
9-
import { GitHubLogoIcon } from '@radix-ui/react-icons';
10-
import { Menu } from 'lucide-react';
10+
import {
11+
Menu,
12+
Github,
13+
LogOut,
14+
Trash2,
15+
FileDown,
16+
FileJson,
17+
FileText,
18+
} from 'lucide-react';
1119
import { ModeToggle } from '../../utils/theme-mode-toggle';
1220
import { buttonVariants } from '@/components/ui/button';
1321
import {
@@ -16,73 +24,140 @@ import {
1624
handleLogout,
1725
RouteProps,
1826
Props,
19-
} from './navbar-utils';
27+
} from '@/components/HomeComponents/Navbar/navbar-utils';
28+
import {
29+
Dialog,
30+
DialogContent,
31+
DialogDescription,
32+
DialogHeader,
33+
DialogTitle,
34+
DialogTrigger,
35+
} from '@/components/ui/dialog';
36+
import { Button } from '@/components/ui/button';
37+
import { exportTasksAsJSON, exportTasksAsTXT } from '@/exports-tasks';
2038

2139
export const NavbarMobile = (
2240
props: Props & { setIsOpen: (isOpen: boolean) => void; isOpen: boolean } & {
2341
isLoading: boolean;
2442
setIsLoading: (val: boolean) => void;
2543
}
26-
) => (
27-
<span className="flex md:hidden">
28-
<ModeToggle />
44+
) => {
45+
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
2946

30-
<Sheet open={props.isOpen} onOpenChange={props.setIsOpen}>
31-
<SheetTrigger className="px-2">
32-
<Menu
33-
className="flex md:hidden h-5 w-5"
34-
onClick={() => props.setIsOpen(true)}
35-
>
36-
<span className="sr-only">Menu Icon</span>
37-
</Menu>
38-
</SheetTrigger>
47+
const handleExportJSON = () => {
48+
exportTasksAsJSON(props.tasks || []);
49+
setIsExportDialogOpen(false);
50+
props.setIsOpen(false);
51+
};
3952

40-
<SheetContent side={'left'}>
41-
<SheetHeader>
42-
<SheetTitle className="font-bold text-xl">CCSync</SheetTitle>
43-
</SheetHeader>
53+
const handleExportTXT = () => {
54+
exportTasksAsTXT(props.tasks || []);
55+
setIsExportDialogOpen(false);
56+
props.setIsOpen(false);
57+
};
4458

45-
<nav className="flex flex-col justify-center items-center gap-2 mt-4">
46-
{routeList.map(({ href, label }: RouteProps) => (
47-
<a
48-
rel="noreferrer noopener"
49-
key={label}
50-
href={href}
51-
onClick={() => props.setIsOpen(false)}
52-
className={buttonVariants({ variant: 'ghost' })}
53-
>
54-
{label}
55-
</a>
56-
))}
57-
<a
58-
rel="noreferrer noopener"
59-
href={url.githubRepoURL}
60-
target="_blank"
61-
className={`w-[110px] border ${buttonVariants({
62-
variant: 'secondary',
63-
})}`}
64-
>
65-
<GitHubLogoIcon className="mr-2 w-5 h-5" />
66-
Github
67-
</a>
68-
<div
69-
onClick={() => deleteAllTasks(props)}
70-
className={`w-[110px] border ${buttonVariants({
71-
variant: 'destructive',
72-
})}`}
59+
return (
60+
<span className="flex md:hidden">
61+
<ModeToggle />
62+
63+
<Sheet open={props.isOpen} onOpenChange={props.setIsOpen}>
64+
<SheetTrigger className="px-2">
65+
<Menu
66+
className="flex md:hidden h-5 w-5"
67+
onClick={() => props.setIsOpen(true)}
7368
>
74-
Delete All Tasks
75-
</div>
76-
<div
77-
onClick={handleLogout}
78-
className={`w-[110px] border ${buttonVariants({
79-
variant: 'destructive',
80-
})}`}
69+
<span className="sr-only">Menu Icon</span>
70+
</Menu>
71+
</SheetTrigger>
72+
<SheetContent side={'left'}>
73+
<SheetHeader>
74+
<SheetTitle className="font-bold text-xl">CCSync</SheetTitle>
75+
</SheetHeader>
76+
<Dialog
77+
open={isExportDialogOpen}
78+
onOpenChange={setIsExportDialogOpen}
8179
>
82-
Log out
83-
</div>
84-
</nav>
85-
</SheetContent>
86-
</Sheet>
87-
</span>
88-
);
80+
<nav className="flex flex-col justify-center items-center gap-2 mt-4">
81+
{routeList.map(({ href, label }: RouteProps) => (
82+
<a
83+
rel="noreferrer noopener"
84+
key={label}
85+
href={href}
86+
onClick={() => props.setIsOpen(false)}
87+
className={buttonVariants({ variant: 'ghost' })}
88+
>
89+
{label}
90+
</a>
91+
))}
92+
<a
93+
rel="noreferrer noopener"
94+
href={url.githubRepoURL}
95+
target="_blank"
96+
className={`w-[130px] border ${buttonVariants({
97+
variant: 'secondary',
98+
})}`}
99+
>
100+
<Github className="mr-2 w-5 h-5" />
101+
Github
102+
</a>
103+
104+
<DialogTrigger asChild>
105+
<div
106+
className={`w-[130px] cursor-pointer border ${buttonVariants({
107+
variant: 'secondary',
108+
})}`}
109+
>
110+
<FileDown className="mr-2 w-5 h-5" />
111+
Export Tasks
112+
</div>
113+
</DialogTrigger>
114+
<div
115+
onClick={() => deleteAllTasks(props)}
116+
className={`w-[130px] border ${buttonVariants({
117+
variant: 'destructive',
118+
})}`}
119+
>
120+
<Trash2 className="mr-2 w-5 h-5" />
121+
Delete All Tasks
122+
</div>
123+
<div
124+
onClick={handleLogout}
125+
className={`w-[130px] border ${buttonVariants({
126+
variant: 'destructive',
127+
})}`}
128+
>
129+
<LogOut className="mr-2 w-5 h-5" />
130+
Log out
131+
</div>
132+
</nav>
133+
<DialogContent>
134+
<DialogHeader>
135+
<DialogTitle>Choose Export Format</DialogTitle>
136+
<DialogDescription>
137+
Would you like to download your tasks as a JSON file or a TXT
138+
file?
139+
</DialogDescription>
140+
</DialogHeader>
141+
<div className="flex flex-col sm:flex-row justify-end gap-2 mt-4">
142+
<Button
143+
onClick={handleExportTXT}
144+
className="w-full sm:w-auto hover:bg-white bg-[#3B82F6]"
145+
>
146+
<FileText className="mr-2 h-4 w-4" />
147+
Download .txt
148+
</Button>
149+
<Button
150+
onClick={handleExportJSON}
151+
className="w-full sm:w-auto hover:bg-white bg-[#3B82F6]"
152+
>
153+
<FileJson className="mr-2 h-4 w-4" />
154+
Download .json
155+
</Button>
156+
</div>
157+
</DialogContent>
158+
</Dialog>
159+
</SheetContent>
160+
</Sheet>
161+
</span>
162+
);
163+
};

0 commit comments

Comments
 (0)