summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-08-13 10:58:20 +0100
committerdiogo464 <[email protected]>2025-08-13 10:58:20 +0100
commitdaca3c076675b43fcae8c362ddd9e922bb0f5e9d (patch)
tree8acfdf0b791727bb6699f2b7c238628a0f3f766e /frontend
parent5c48d5cc58ce5d296d770c0e16cca13204b8200f (diff)
Add Create Folder functionality with dialog interface
- New /api/mkdir endpoint for authenticated folder creation - Create Folder button positioned left of Upload Files button - Modal dialog with input validation and keyboard shortcuts - Proper error handling and success notifications - Uses existing CLI Drive_mkdir function for backend operations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Diffstat (limited to 'frontend')
-rw-r--r--frontend/app/api/mkdir/route.ts54
-rw-r--r--frontend/components/drive/DriveDirectoryClient.tsx83
2 files changed, 137 insertions, 0 deletions
diff --git a/frontend/app/api/mkdir/route.ts b/frontend/app/api/mkdir/route.ts
new file mode 100644
index 0000000..18e1bbc
--- /dev/null
+++ b/frontend/app/api/mkdir/route.ts
@@ -0,0 +1,54 @@
1import { NextRequest, NextResponse } from 'next/server'
2import { Auth_get_user } from '@/lib/auth'
3import { Drive_mkdir } from '@/lib/drive_server'
4
5// POST /api/mkdir - Create directory
6export async function POST(request: NextRequest) {
7 try {
8 // Check user authentication
9 const user = await Auth_get_user()
10 if (!user.isLoggedIn) {
11 return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
12 }
13
14 // Parse request body to get directory info
15 const body = await request.json()
16 const { path, name } = body
17
18 if (!path || typeof path !== 'string') {
19 return NextResponse.json({ error: 'Path is required' }, { status: 400 })
20 }
21
22 if (!name || typeof name !== 'string' || name.trim() === '') {
23 return NextResponse.json({ error: 'Directory name is required' }, { status: 400 })
24 }
25
26 // Construct full directory path
27 const dirName = name.trim()
28 const fullPath = path === '/' ? `/${dirName}` : `${path}/${dirName}`
29
30 // Create directory using Drive_mkdir
31 try {
32 await Drive_mkdir(fullPath, user.email)
33
34 return NextResponse.json({
35 success: true,
36 message: `Directory "${dirName}" created successfully`,
37 path: fullPath
38 })
39
40 } catch (error) {
41 console.error(`Failed to create directory ${fullPath}:`, error)
42 return NextResponse.json({
43 error: error instanceof Error ? error.message : 'Failed to create directory'
44 }, { status: 500 })
45 }
46
47 } catch (error) {
48 console.error('Mkdir API error:', error)
49 return NextResponse.json(
50 { error: error instanceof Error ? error.message : 'Internal server error' },
51 { status: 500 }
52 )
53 }
54} \ No newline at end of file
diff --git a/frontend/components/drive/DriveDirectoryClient.tsx b/frontend/components/drive/DriveDirectoryClient.tsx
index 4657141..eee83c3 100644
--- a/frontend/components/drive/DriveDirectoryClient.tsx
+++ b/frontend/components/drive/DriveDirectoryClient.tsx
@@ -17,6 +17,7 @@ import {
17 Info, 17 Info,
18 LogIn, 18 LogIn,
19 LogOut, 19 LogOut,
20 FolderPlus,
20} from "lucide-react" 21} from "lucide-react"
21import { Button } from "@/components/ui/button" 22import { Button } from "@/components/ui/button"
22import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" 23import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
@@ -77,8 +78,10 @@ export function DriveDirectoryClient({ path, files, breadcrumbs }: DriveDirector
77 const [renameDialogOpen, setRenameDialogOpen] = useState(false) 78 const [renameDialogOpen, setRenameDialogOpen] = useState(false)
78 const [infoDialogOpen, setInfoDialogOpen] = useState(false) 79 const [infoDialogOpen, setInfoDialogOpen] = useState(false)
79 const [moveDialogOpen, setMoveDialogOpen] = useState(false) 80 const [moveDialogOpen, setMoveDialogOpen] = useState(false)
81 const [createFolderDialogOpen, setCreateFolderDialogOpen] = useState(false)
80 const [currentItem, setCurrentItem] = useState<DriveLsEntry | null>(null) 82 const [currentItem, setCurrentItem] = useState<DriveLsEntry | null>(null)
81 const [newName, setNewName] = useState("") 83 const [newName, setNewName] = useState("")
84 const [newFolderName, setNewFolderName] = useState("")
82 const fileInputRef = useRef<HTMLInputElement>(null) 85 const fileInputRef = useRef<HTMLInputElement>(null)
83 const [uploading, setUploading] = useState(false) 86 const [uploading, setUploading] = useState(false)
84 87
@@ -294,6 +297,46 @@ export function DriveDirectoryClient({ path, files, breadcrumbs }: DriveDirector
294 window.location.reload() 297 window.location.reload()
295 } 298 }
296 299
300 const handleCreateFolder = async () => {
301 if (!newFolderName.trim()) return
302
303 try {
304 const response = await fetch('/api/mkdir', {
305 method: 'POST',
306 headers: {
307 'Content-Type': 'application/json',
308 },
309 body: JSON.stringify({
310 path: path,
311 name: newFolderName.trim()
312 })
313 })
314
315 const result = await response.json()
316
317 if (response.ok) {
318 setCreateFolderDialogOpen(false)
319 setNewFolderName("")
320 toast({
321 title: "Folder created",
322 description: result.message,
323 })
324
325 // Refresh page to show changes
326 window.location.reload()
327 } else {
328 throw new Error(result.error || `Failed to create folder`)
329 }
330 } catch (error) {
331 console.error('Create folder error:', error)
332 toast({
333 title: "Failed to create folder",
334 description: error instanceof Error ? error.message : 'Unknown error occurred',
335 variant: "destructive"
336 })
337 }
338 }
339
297 return ( 340 return (
298 <div className="container mx-auto p-6 space-y-6"> 341 <div className="container mx-auto p-6 space-y-6">
299 {/* Header with Breadcrumbs */} 342 {/* Header with Breadcrumbs */}
@@ -326,6 +369,13 @@ export function DriveDirectoryClient({ path, files, breadcrumbs }: DriveDirector
326 369
327 <div className="flex items-center gap-2"> 370 <div className="flex items-center gap-2">
328 <Button 371 <Button
372 variant="secondary"
373 onClick={() => setCreateFolderDialogOpen(true)}
374 >
375 <FolderPlus className="mr-2 h-4 w-4" />
376 Create Folder
377 </Button>
378 <Button
329 onClick={() => fileInputRef.current?.click()} 379 onClick={() => fileInputRef.current?.click()}
330 disabled={uploading} 380 disabled={uploading}
331 > 381 >
@@ -593,6 +643,39 @@ export function DriveDirectoryClient({ path, files, breadcrumbs }: DriveDirector
593 onMove={handleMove} 643 onMove={handleMove}
594 /> 644 />
595 645
646 {/* Create Folder Dialog */}
647 <Dialog open={createFolderDialogOpen} onOpenChange={setCreateFolderDialogOpen}>
648 <DialogContent>
649 <DialogHeader>
650 <DialogTitle>Create New Folder</DialogTitle>
651 </DialogHeader>
652 <div className="space-y-4 pt-2">
653 <div className="space-y-2">
654 <Label htmlFor="folderName">Folder Name</Label>
655 <Input
656 id="folderName"
657 value={newFolderName}
658 onChange={(e) => setNewFolderName(e.target.value)}
659 onKeyDown={(e) => {
660 if (e.key === "Enter") {
661 handleCreateFolder()
662 }
663 }}
664 placeholder="Enter folder name"
665 />
666 </div>
667 <div className="flex justify-end gap-2">
668 <Button variant="outline" onClick={() => setCreateFolderDialogOpen(false)}>
669 Cancel
670 </Button>
671 <Button onClick={handleCreateFolder} disabled={!newFolderName.trim()}>
672 Create Folder
673 </Button>
674 </div>
675 </div>
676 </DialogContent>
677 </Dialog>
678
596 <input 679 <input
597 ref={fileInputRef} 680 ref={fileInputRef}
598 type="file" 681 type="file"