summaryrefslogtreecommitdiff
path: root/frontend/components
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-08-14 21:11:48 +0100
committerdiogo464 <[email protected]>2025-08-14 21:13:57 +0100
commite4917874be67de24f934e069b53e1726599c6cc5 (patch)
tree92079699e1f17e0a088d110f6ab19fb093ba3adf /frontend/components
parentf1edb3ce07ce5569718f6a899679e24f45349ce2 (diff)
frontend: improvements
Diffstat (limited to 'frontend/components')
-rw-r--r--frontend/components/drive/DriveDirectoryClient.tsx88
-rw-r--r--frontend/components/drive/DriveDirectoryView.tsx7
-rw-r--r--frontend/components/drive/StorageUsage.tsx9
-rw-r--r--frontend/components/history/HistoryView.tsx9
4 files changed, 39 insertions, 74 deletions
diff --git a/frontend/components/drive/DriveDirectoryClient.tsx b/frontend/components/drive/DriveDirectoryClient.tsx
index f523e4f..d238065 100644
--- a/frontend/components/drive/DriveDirectoryClient.tsx
+++ b/frontend/components/drive/DriveDirectoryClient.tsx
@@ -32,18 +32,10 @@ import { Checkbox } from "@/components/ui/checkbox"
32import { toast } from "@/hooks/use-toast" 32import { toast } from "@/hooks/use-toast"
33import { DriveLsEntry } from "@/lib/drive_types" 33import { DriveLsEntry } from "@/lib/drive_types"
34import { UPLOAD_MAX_FILE_SIZE, UPLOAD_MAX_FILES } from "@/lib/constants" 34import { UPLOAD_MAX_FILE_SIZE, UPLOAD_MAX_FILES } from "@/lib/constants"
35import { formatFileSize } from "@/lib/utils"
35import { DriveMoveDialog } from "./DriveMoveDialog" 36import { DriveMoveDialog } from "./DriveMoveDialog"
36import { StorageUsage } from "./StorageUsage" 37import { StorageUsage } from "./StorageUsage"
37import type { StorageData } from "@/lib/storage" 38import type { StorageData } from "@/lib/storage"
38import type { UserAuth } from "@/lib/auth_types"
39
40function formatFileSize(bytes: number): string {
41 if (bytes === 0) return "0 Bytes"
42 const k = 1024
43 const sizes = ["Bytes", "KB", "MB", "GB"]
44 const i = Math.floor(Math.log(bytes) / Math.log(k))
45 return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
46}
47 39
48function formatDate(timestamp: number): string { 40function formatDate(timestamp: number): string {
49 return new Date(timestamp * 1000).toISOString().split('T')[0] 41 return new Date(timestamp * 1000).toISOString().split('T')[0]
@@ -52,9 +44,9 @@ function formatDate(timestamp: number): string {
52function formatDateTime(timestamp: number): string { 44function formatDateTime(timestamp: number): string {
53 const date = new Date(timestamp * 1000) 45 const date = new Date(timestamp * 1000)
54 const dateStr = date.toISOString().split('T')[0] 46 const dateStr = date.toISOString().split('T')[0]
55 const timeStr = date.toLocaleTimeString('en-US', { 47 const timeStr = date.toLocaleTimeString('en-US', {
56 hour12: false, 48 hour12: false,
57 hour: '2-digit', 49 hour: '2-digit',
58 minute: '2-digit', 50 minute: '2-digit',
59 second: '2-digit' 51 second: '2-digit'
60 }) 52 })
@@ -72,10 +64,9 @@ interface DriveDirectoryClientProps {
72 files: DriveLsEntry[] 64 files: DriveLsEntry[]
73 breadcrumbs: Breadcrumb[] 65 breadcrumbs: Breadcrumb[]
74 storageData: StorageData 66 storageData: StorageData
75 user: UserAuth
76} 67}
77 68
78export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, user }: DriveDirectoryClientProps) { 69export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: DriveDirectoryClientProps) {
79 const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set()) 70 const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set())
80 const [renameDialogOpen, setRenameDialogOpen] = useState(false) 71 const [renameDialogOpen, setRenameDialogOpen] = useState(false)
81 const [infoDialogOpen, setInfoDialogOpen] = useState(false) 72 const [infoDialogOpen, setInfoDialogOpen] = useState(false)
@@ -98,10 +89,6 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
98 setSelectedFiles(newSelected) 89 setSelectedFiles(newSelected)
99 } 90 }
100 91
101 const selectAll = () => {
102 setSelectedFiles(new Set(files.map(file => file.path)))
103 }
104
105 const deselectAll = () => { 92 const deselectAll = () => {
106 setSelectedFiles(new Set()) 93 setSelectedFiles(new Set())
107 } 94 }
@@ -130,7 +117,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
130 }) 117 })
131 return 118 return
132 } 119 }
133 120
134 const filename = item.path.split('/').pop() || 'download' 121 const filename = item.path.split('/').pop() || 'download'
135 const permalink = `${window.location.origin}/blob/${item.blob}?filename=${encodeURIComponent(filename)}` 122 const permalink = `${window.location.origin}/blob/${item.blob}?filename=${encodeURIComponent(filename)}`
136 navigator.clipboard.writeText(permalink).then(() => { 123 navigator.clipboard.writeText(permalink).then(() => {
@@ -171,8 +158,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
171 title: "Renamed successfully", 158 title: "Renamed successfully",
172 description: result.message, 159 description: result.message,
173 }) 160 })
174 161
175 // Refresh page to show changes
176 window.location.reload() 162 window.location.reload()
177 } else { 163 } else {
178 throw new Error(result.error || `Rename failed with status ${response.status}`) 164 throw new Error(result.error || `Rename failed with status ${response.status}`)
@@ -222,7 +208,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
222 try { 208 try {
223 const formData = new FormData() 209 const formData = new FormData()
224 formData.append('file', file) 210 formData.append('file', file)
225 211
226 // Use the new simple upload endpoint with path as query parameter 212 // Use the new simple upload endpoint with path as query parameter
227 const response = await fetch(`/api/upload?path=${encodeURIComponent(path)}`, { 213 const response = await fetch(`/api/upload?path=${encodeURIComponent(path)}`, {
228 method: 'POST', 214 method: 'POST',
@@ -247,8 +233,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
247 title: "Upload successful", 233 title: "Upload successful",
248 description: `${successCount} file(s) uploaded successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}` 234 description: `${successCount} file(s) uploaded successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}`
249 }) 235 })
250 236
251 // Refresh page to show changes
252 window.location.reload() 237 window.location.reload()
253 } 238 }
254 239
@@ -292,8 +277,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
292 title: "Deleted successfully", 277 title: "Deleted successfully",
293 description: result.message, 278 description: result.message,
294 }) 279 })
295 280
296 // Refresh page to show changes
297 window.location.reload() 281 window.location.reload()
298 } else { 282 } else {
299 throw new Error(result.error || `Delete failed with status ${response.status}`) 283 throw new Error(result.error || `Delete failed with status ${response.status}`)
@@ -322,7 +306,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
322 // Extract filename from the full path 306 // Extract filename from the full path
323 const fileName = filePath.split('/').pop() || '' 307 const fileName = filePath.split('/').pop() || ''
324 // Construct new path: destination + filename 308 // Construct new path: destination + filename
325 const newPath = destinationPath.endsWith('/') 309 const newPath = destinationPath.endsWith('/')
326 ? `${destinationPath}${fileName}` 310 ? `${destinationPath}${fileName}`
327 : `${destinationPath}/${fileName}` 311 : `${destinationPath}/${fileName}`
328 312
@@ -360,14 +344,13 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
360 title: "Move completed", 344 title: "Move completed",
361 description: `${successCount} item(s) moved successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}`, 345 description: `${successCount} item(s) moved successfully${errorCount > 0 ? `, ${errorCount} failed` : ''}`,
362 }) 346 })
363 347
364 // Refresh page to show changes
365 window.location.reload() 348 window.location.reload()
366 } 349 }
367 350
368 if (errorCount > 0 && successCount === 0) { 351 if (errorCount > 0 && successCount === 0) {
369 toast({ 352 toast({
370 title: "Move failed", 353 title: "Move failed",
371 description: `All ${errorCount} item(s) failed to move. ${errors[0] || ''}`, 354 description: `All ${errorCount} item(s) failed to move. ${errors[0] || ''}`,
372 variant: "destructive" 355 variant: "destructive"
373 }) 356 })
@@ -416,8 +399,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
416 title: "Folder created", 399 title: "Folder created",
417 description: result.message, 400 description: result.message,
418 }) 401 })
419 402
420 // Refresh page to show changes
421 window.location.reload() 403 window.location.reload()
422 } else { 404 } else {
423 throw new Error(result.error || `Failed to create folder`) 405 throw new Error(result.error || `Failed to create folder`)
@@ -447,7 +429,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
447 {index === breadcrumbs.length - 1 ? ( 429 {index === breadcrumbs.length - 1 ? (
448 <span className="text-foreground font-medium">{crumb.name}</span> 430 <span className="text-foreground font-medium">{crumb.name}</span>
449 ) : ( 431 ) : (
450 <Link 432 <Link
451 href={crumb.path} 433 href={crumb.path}
452 className="hover:text-foreground transition-colors" 434 className="hover:text-foreground transition-colors"
453 > 435 >
@@ -457,9 +439,9 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
457 </div> 439 </div>
458 ))} 440 ))}
459 </nav> 441 </nav>
460 442
461 <div className="flex items-center gap-2 sm:gap-2"> 443 <div className="flex items-center gap-2 sm:gap-2">
462 <Button 444 <Button
463 variant="secondary" 445 variant="secondary"
464 onClick={() => setCreateFolderDialogOpen(true)} 446 onClick={() => setCreateFolderDialogOpen(true)}
465 className="flex-1 sm:flex-none" 447 className="flex-1 sm:flex-none"
@@ -467,7 +449,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
467 <FolderPlus className="mr-2 h-4 w-4" /> 449 <FolderPlus className="mr-2 h-4 w-4" />
468 Create Folder 450 Create Folder
469 </Button> 451 </Button>
470 <Button 452 <Button
471 onClick={() => fileInputRef.current?.click()} 453 onClick={() => fileInputRef.current?.click()}
472 disabled={uploading} 454 disabled={uploading}
473 className="flex-1 sm:flex-none" 455 className="flex-1 sm:flex-none"
@@ -534,16 +516,16 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
534 files.map((file) => { 516 files.map((file) => {
535 const isSelected = selectedFiles.has(file.path) 517 const isSelected = selectedFiles.has(file.path)
536 const fileName = file.path.split('/').pop() || file.path 518 const fileName = file.path.split('/').pop() || file.path
537 519
538 return ( 520 return (
539 <TableRow 521 <TableRow
540 key={file.path} 522 key={file.path}
541 className={`hover:bg-muted/50 ${isSelected ? "bg-muted/30" : ""}`} 523 className={`hover:bg-muted/50 ${isSelected ? "bg-muted/30" : ""}`}
542 > 524 >
543 <TableCell className="w-[40px]" onClick={(e) => e.stopPropagation()}> 525 <TableCell className="w-[40px]" onClick={(e) => e.stopPropagation()}>
544 <Checkbox 526 <Checkbox
545 checked={isSelected} 527 checked={isSelected}
546 onCheckedChange={() => toggleFileSelection(file.path)} 528 onCheckedChange={() => toggleFileSelection(file.path)}
547 /> 529 />
548 </TableCell> 530 </TableCell>
549 <TableCell className="font-medium"> 531 <TableCell className="font-medium">
@@ -552,7 +534,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
552 <> 534 <>
553 <Folder className="h-4 w-4 text-blue-500 flex-shrink-0" /> 535 <Folder className="h-4 w-4 text-blue-500 flex-shrink-0" />
554 <div className="min-w-0 max-w-[60vw]"> 536 <div className="min-w-0 max-w-[60vw]">
555 <Link 537 <Link
556 href={`/drive${file.path}`} 538 href={`/drive${file.path}`}
557 className="text-blue-600 hover:text-blue-800 hover:underline cursor-pointer block truncate" 539 className="text-blue-600 hover:text-blue-800 hover:underline cursor-pointer block truncate"
558 title={fileName} 540 title={fileName}
@@ -606,8 +588,8 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
606 Info 588 Info
607 </DropdownMenuItem> 589 </DropdownMenuItem>
608 <DropdownMenuSeparator /> 590 <DropdownMenuSeparator />
609 <DropdownMenuItem 591 <DropdownMenuItem
610 onClick={() => handleDelete([file.path])} 592 onClick={() => handleDelete([file.path])}
611 className="text-red-600" 593 className="text-red-600"
612 > 594 >
613 <Trash2 className="mr-2 h-4 w-4" /> 595 <Trash2 className="mr-2 h-4 w-4" />
@@ -695,7 +677,7 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
695 </div> 677 </div>
696 <div> 678 <div>
697 <Label className="text-sm font-medium text-muted-foreground">Path</Label> 679 <Label className="text-sm font-medium text-muted-foreground">Path</Label>
698 <p className="text-sm font-mono text-xs">{currentItem.path}</p> 680 <p className="font-mono text-xs">{currentItem.path}</p>
699 </div> 681 </div>
700 </div> 682 </div>
701 <div className="flex justify-end"> 683 <div className="flex justify-end">
@@ -749,13 +731,13 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, us
749 </DialogContent> 731 </DialogContent>
750 </Dialog> 732 </Dialog>
751 733
752 <input 734 <input
753 ref={fileInputRef} 735 ref={fileInputRef}
754 type="file" 736 type="file"
755 multiple 737 multiple
756 className="hidden" 738 className="hidden"
757 onChange={handleFileUpload} 739 onChange={handleFileUpload}
758 /> 740 />
759 </div> 741 </div>
760 ) 742 )
761} \ No newline at end of file 743}
diff --git a/frontend/components/drive/DriveDirectoryView.tsx b/frontend/components/drive/DriveDirectoryView.tsx
index d2f30ee..de9d70a 100644
--- a/frontend/components/drive/DriveDirectoryView.tsx
+++ b/frontend/components/drive/DriveDirectoryView.tsx
@@ -2,7 +2,6 @@ import { DriveLsEntry } from "@/lib/drive_types"
2import { DriveDirectoryClient } from "./DriveDirectoryClient" 2import { DriveDirectoryClient } from "./DriveDirectoryClient"
3import { DriveHeader } from "./DriveHeader" 3import { DriveHeader } from "./DriveHeader"
4import type { StorageData } from "@/lib/storage" 4import type { StorageData } from "@/lib/storage"
5import { Auth_get_user } from "@/lib/auth"
6 5
7interface DriveDirectoryViewProps { 6interface DriveDirectoryViewProps {
8 path: string 7 path: string
@@ -20,7 +19,7 @@ function generateBreadcrumbs(currentPath: string) {
20 const breadcrumbs = [{ name: 'Root', path: '/drive' }] 19 const breadcrumbs = [{ name: 'Root', path: '/drive' }]
21 20
22 let accumulatedPath = '' 21 let accumulatedPath = ''
23 parts.forEach((part, index) => { 22 parts.forEach((part, _index) => {
24 accumulatedPath += '/' + part 23 accumulatedPath += '/' + part
25 breadcrumbs.push({ 24 breadcrumbs.push({
26 name: decodeURIComponent(part), // Decode URL encoded characters 25 name: decodeURIComponent(part), // Decode URL encoded characters
@@ -48,7 +47,6 @@ function sortFiles(files: DriveLsEntry[]): DriveLsEntry[] {
48export async function DriveDirectoryView({ path, files, storageData }: DriveDirectoryViewProps) { 47export async function DriveDirectoryView({ path, files, storageData }: DriveDirectoryViewProps) {
49 const sortedFiles = sortFiles(files) 48 const sortedFiles = sortFiles(files)
50 const breadcrumbs = generateBreadcrumbs(path) 49 const breadcrumbs = generateBreadcrumbs(path)
51 const user = await Auth_get_user()
52 50
53 return ( 51 return (
54 <div className="container mx-auto p-6 space-y-6"> 52 <div className="container mx-auto p-6 space-y-6">
@@ -58,8 +56,7 @@ export async function DriveDirectoryView({ path, files, storageData }: DriveDire
58 files={sortedFiles} 56 files={sortedFiles}
59 breadcrumbs={breadcrumbs} 57 breadcrumbs={breadcrumbs}
60 storageData={storageData} 58 storageData={storageData}
61 user={user}
62 /> 59 />
63 </div> 60 </div>
64 ) 61 )
65} \ No newline at end of file 62}
diff --git a/frontend/components/drive/StorageUsage.tsx b/frontend/components/drive/StorageUsage.tsx
index 2cb5d5d..39be4ba 100644
--- a/frontend/components/drive/StorageUsage.tsx
+++ b/frontend/components/drive/StorageUsage.tsx
@@ -1,17 +1,10 @@
1import type { StorageData } from "@/lib/storage" 1import type { StorageData } from "@/lib/storage"
2import { formatFileSize } from "@/lib/utils"
2 3
3interface StorageUsageProps { 4interface StorageUsageProps {
4 data: StorageData 5 data: StorageData
5} 6}
6 7
7function formatFileSize(bytes: number): string {
8 if (bytes === 0) return "0 Bytes"
9 const k = 1024
10 const sizes = ["Bytes", "KB", "MB", "GB", "TB"]
11 const i = Math.floor(Math.log(bytes) / Math.log(k))
12 return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
13}
14
15export function StorageUsage({ data }: StorageUsageProps) { 8export function StorageUsage({ data }: StorageUsageProps) {
16 // Safety check for undefined data 9 // Safety check for undefined data
17 if (!data) { 10 if (!data) {
diff --git a/frontend/components/history/HistoryView.tsx b/frontend/components/history/HistoryView.tsx
index c459275..e6d1cd1 100644
--- a/frontend/components/history/HistoryView.tsx
+++ b/frontend/components/history/HistoryView.tsx
@@ -3,6 +3,7 @@ import { DriveHeader } from "@/components/drive/DriveHeader"
3import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" 3import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
4import { Button } from "@/components/ui/button" 4import { Button } from "@/components/ui/button"
5import { ChevronLeft, ChevronRight } from "lucide-react" 5import { ChevronLeft, ChevronRight } from "lucide-react"
6import { formatFileSize } from "@/lib/utils"
6import Link from "next/link" 7import Link from "next/link"
7 8
8interface HistoryViewProps { 9interface HistoryViewProps {
@@ -13,14 +14,6 @@ interface HistoryViewProps {
13 totalEntries: number 14 totalEntries: number
14} 15}
15 16
16function formatFileSize(bytes: number): string {
17 if (bytes === 0) return "0 Bytes"
18 const k = 1024
19 const sizes = ["Bytes", "KB", "MB", "GB"]
20 const i = Math.floor(Math.log(bytes) / Math.log(k))
21 return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
22}
23
24function formatDateTime(timestamp: number): string { 17function formatDateTime(timestamp: number): string {
25 return new Date(timestamp * 1000).toLocaleString() 18 return new Date(timestamp * 1000).toLocaleString()
26} 19}