Skip to content

Commit 2dc28b9

Browse files
authored
Merge pull request #92 from zecrypt-io/feature/ananducv/drive-1
Feature/ananducv/drive
2 parents ce924b6 + 717c70c commit 2dc28b9

23 files changed

+5712
-3
lines changed

packages/frontend-web/DRIVE_IMPLEMENTATION.md

Lines changed: 1590 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { DashboardLayout } from "@/components/dashboard-layout";
2+
import { DriveContent } from "@/components/drive-content";
3+
4+
export default async function DrivePage({ params }: { params: Promise<{ locale: string }> }) {
5+
const { locale } = await params;
6+
7+
return (
8+
<DashboardLayout locale={locale}>
9+
<DriveContent />
10+
</DashboardLayout>
11+
);
12+
}
13+
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { Button } from "@/components/ui/button";
5+
import { Input } from "@/components/ui/input";
6+
import { Label } from "@/components/ui/label";
7+
import { AlertCircle } from "lucide-react";
8+
import { useTranslator } from "@/hooks/use-translations";
9+
import {
10+
Dialog,
11+
DialogContent,
12+
DialogDescription,
13+
DialogFooter,
14+
DialogHeader,
15+
DialogTitle,
16+
} from "@/components/ui/dialog";
17+
18+
interface AddFolderDialogProps {
19+
open: boolean;
20+
onOpenChange: (open: boolean) => void;
21+
onFolderCreated: () => void;
22+
parentFolder: { folder_id: string; name: string } | null;
23+
onCreateFolder: (name: string, parentId: string | null) => Promise<void>;
24+
}
25+
26+
export function AddFolderDialog({
27+
open,
28+
onOpenChange,
29+
onFolderCreated,
30+
parentFolder,
31+
onCreateFolder,
32+
}: AddFolderDialogProps) {
33+
const { translate } = useTranslator();
34+
const [folderName, setFolderName] = useState("");
35+
const [error, setError] = useState("");
36+
const [isSubmitting, setIsSubmitting] = useState(false);
37+
38+
const validateForm = () => {
39+
if (!folderName.trim()) {
40+
setError(
41+
translate("folder_name_required", "drive", {
42+
default: "Folder name is required",
43+
})
44+
);
45+
return false;
46+
}
47+
setError("");
48+
return true;
49+
};
50+
51+
const handleSubmit = async () => {
52+
if (!validateForm()) return;
53+
54+
setIsSubmitting(true);
55+
try {
56+
await onCreateFolder(folderName, parentFolder?.folder_id || null);
57+
resetForm();
58+
onFolderCreated();
59+
onOpenChange(false);
60+
} catch (error) {
61+
console.error("Error creating folder:", error);
62+
setError(
63+
translate("error_creating_folder", "drive", {
64+
default: "Error creating folder. Please try again.",
65+
})
66+
);
67+
} finally {
68+
setIsSubmitting(false);
69+
}
70+
};
71+
72+
const resetForm = () => {
73+
setFolderName("");
74+
setError("");
75+
};
76+
77+
useEffect(() => {
78+
if (open) {
79+
resetForm();
80+
}
81+
}, [open]);
82+
83+
return (
84+
<Dialog open={open} onOpenChange={onOpenChange}>
85+
<DialogContent className="sm:max-w-[450px]">
86+
<DialogHeader>
87+
<DialogTitle>
88+
{translate("create_folder", "drive", { default: "Create Folder" })}
89+
</DialogTitle>
90+
<DialogDescription>
91+
{parentFolder
92+
? translate("create_subfolder_in", "drive", {
93+
default: `Create a new folder in "${parentFolder.name}"`,
94+
folderName: parentFolder.name,
95+
})
96+
: translate("create_new_folder", "drive", {
97+
default: "Create a new folder in root",
98+
})}
99+
</DialogDescription>
100+
</DialogHeader>
101+
102+
<div className="space-y-4 py-2 pb-4">
103+
{error && (
104+
<div className="bg-destructive/15 text-destructive p-3 rounded-md flex items-start">
105+
<AlertCircle className="h-5 w-5 mr-2 mt-0.5" />
106+
<div>{error}</div>
107+
</div>
108+
)}
109+
110+
<div className="space-y-2">
111+
<Label htmlFor="folderName">
112+
{translate("folder_name", "drive", { default: "Folder Name" })}
113+
<span className="text-destructive"> *</span>
114+
</Label>
115+
<Input
116+
id="folderName"
117+
placeholder={translate("folder_name_placeholder", "drive", {
118+
default: "My Folder",
119+
})}
120+
value={folderName}
121+
onChange={(e) => setFolderName(e.target.value)}
122+
onKeyDown={(e) => {
123+
if (e.key === "Enter" && !isSubmitting) {
124+
handleSubmit();
125+
}
126+
}}
127+
autoFocus
128+
/>
129+
</div>
130+
</div>
131+
132+
<DialogFooter>
133+
<Button
134+
variant="outline"
135+
onClick={() => onOpenChange(false)}
136+
disabled={isSubmitting}
137+
>
138+
{translate("cancel", "drive", { default: "Cancel" })}
139+
</Button>
140+
<Button onClick={handleSubmit} disabled={isSubmitting}>
141+
{isSubmitting
142+
? translate("creating", "drive", { default: "Creating..." })
143+
: translate("create", "drive", { default: "Create" })}
144+
</Button>
145+
</DialogFooter>
146+
</DialogContent>
147+
</Dialog>
148+
);
149+
}
150+
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import {
5+
AlertDialog,
6+
AlertDialogAction,
7+
AlertDialogCancel,
8+
AlertDialogContent,
9+
AlertDialogDescription,
10+
AlertDialogFooter,
11+
AlertDialogHeader,
12+
AlertDialogTitle,
13+
} from "@/components/ui/alert-dialog";
14+
import { useTranslator } from "@/hooks/use-translations";
15+
16+
interface DriveFile {
17+
file_id: string;
18+
name: string;
19+
}
20+
21+
interface DeleteFileDialogProps {
22+
open: boolean;
23+
onOpenChange: (open: boolean) => void;
24+
file: DriveFile | null;
25+
onDelete: (fileIds: string[]) => Promise<void>;
26+
}
27+
28+
export function DeleteFileDialog({
29+
open,
30+
onOpenChange,
31+
file,
32+
onDelete,
33+
}: DeleteFileDialogProps) {
34+
const { translate } = useTranslator();
35+
const [isDeleting, setIsDeleting] = useState(false);
36+
37+
const handleDelete = async () => {
38+
if (!file) return;
39+
40+
setIsDeleting(true);
41+
42+
try {
43+
await onDelete([file.file_id]);
44+
onOpenChange(false);
45+
} catch (error) {
46+
console.error("Error deleting file:", error);
47+
} finally {
48+
setIsDeleting(false);
49+
}
50+
};
51+
52+
return (
53+
<AlertDialog open={open} onOpenChange={onOpenChange}>
54+
<AlertDialogContent>
55+
<AlertDialogHeader>
56+
<AlertDialogTitle>
57+
{translate("delete_file", "drive", { default: "Delete File" })}
58+
</AlertDialogTitle>
59+
<AlertDialogDescription>
60+
{file && (
61+
<>
62+
{translate("delete_file_confirmation", "drive", {
63+
default: "Are you sure you want to delete this file? This action cannot be undone.",
64+
})}
65+
<br />
66+
<br />
67+
<strong>{file.name}</strong>
68+
</>
69+
)}
70+
</AlertDialogDescription>
71+
</AlertDialogHeader>
72+
<AlertDialogFooter>
73+
<AlertDialogCancel disabled={isDeleting}>
74+
{translate("cancel", "actions", { default: "Cancel" })}
75+
</AlertDialogCancel>
76+
<AlertDialogAction
77+
onClick={(e) => {
78+
e.preventDefault();
79+
handleDelete();
80+
}}
81+
disabled={isDeleting}
82+
className="bg-destructive hover:bg-destructive/90"
83+
>
84+
{isDeleting
85+
? translate("deleting", "drive", { default: "Deleting..." })
86+
: translate("delete", "actions", { default: "Delete" })}
87+
</AlertDialogAction>
88+
</AlertDialogFooter>
89+
</AlertDialogContent>
90+
</AlertDialog>
91+
);
92+
}
93+

0 commit comments

Comments
 (0)