summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-08-11 17:03:31 +0100
committerdiogo464 <[email protected]>2025-08-11 17:03:31 +0100
commitdc269fa3cb1049a14000286e951325ff17a3c5e7 (patch)
tree29bec862436ee7f43d863b1fbf63a4681bdd4cb7 /frontend
parent6b71b7f2365001bf0d474cca1625c82e310abf63 (diff)
Implement history page with real data from Drive_log API
- Removed v0 generated HistoryEntry type and mock data - Updated HistoryView to use DriveLogEntry from our Drive_log API - Added proper loading and error states - Reversed log output to show latest entries first (as requested) - Added formatFileSize utility function to utils.ts and used it for file sizes - Updated action types and badges to match actual drive log actions - Convert Unix timestamps to proper date formatting - Show real user emails, file paths, and file sizes from actual drive log 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Diffstat (limited to 'frontend')
-rw-r--r--frontend/history-view.tsx237
-rw-r--r--frontend/lib/utils.ts8
2 files changed, 98 insertions, 147 deletions
diff --git a/frontend/history-view.tsx b/frontend/history-view.tsx
index 8cec793..2faa1c0 100644
--- a/frontend/history-view.tsx
+++ b/frontend/history-view.tsx
@@ -1,131 +1,15 @@
1"use client" 1"use client"
2 2
3import { useState, useEffect } from "react"
3import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" 4import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
4import { Badge } from "@/components/ui/badge" 5import { Badge } from "@/components/ui/badge"
5import { FileText, Folder, Trash2, Edit } from "lucide-react" 6import { FileText, Folder, Trash2, Edit, Plus } from "lucide-react"
7import { DriveLogEntry } from "@/lib/drive_types"
8import { formatFileSize } from "@/lib/utils"
6 9
7interface HistoryEntry {
8 id: string
9 type: "file_create" | "file_remove" | "dir_create" | "rename"
10 fileName: string
11 userEmail: string
12 timestamp: string
13 details?: string
14}
15
16const mockHistoryData: HistoryEntry[] = [
17 {
18 id: "h1",
19 type: "file_create",
20 fileName: "Database Backup and Migration Scripts - Production Environment.sql",
21 userEmail: "[email protected]",
22 timestamp: "2024-01-16T09:30:00Z",
23 },
24 {
25 id: "h2",
26 type: "rename",
27 fileName: "Configuration Files and System Settings - Development Environment Setup.txt",
28 userEmail: "[email protected]",
29 timestamp: "2024-01-16T09:25:00Z",
30 details: "Renamed from 'config.txt'",
31 },
32 {
33 id: "h3",
34 type: "file_create",
35 fileName: "E-commerce Platform with React and Node.js - Full Stack Implementation.zip",
36 userEmail: "[email protected]",
37 timestamp: "2024-01-16T08:35:00Z",
38 },
39 {
40 id: "h4",
41 type: "dir_create",
42 fileName: "Web Applications and Frontend Projects",
43 userEmail: "[email protected]",
44 timestamp: "2024-01-16T08:30:00Z",
45 },
46 {
47 id: "h5",
48 type: "file_create",
49 fileName: "Dashboard Analytics Tool with Real-time Data Visualization Components.zip",
50 userEmail: "[email protected]",
51 timestamp: "2024-01-15T22:10:00Z",
52 },
53 {
54 id: "h6",
55 type: "file_remove",
56 fileName: "old_backup_file.sql",
57 userEmail: "[email protected]",
58 timestamp: "2024-01-15T20:45:00Z",
59 },
60 {
61 id: "h7",
62 type: "rename",
63 fileName: "Mobile App Development and Cross-Platform Solutions.zip",
64 userEmail: "[email protected]",
65 timestamp: "2024-01-14T13:25:00Z",
66 details: "Renamed from 'mobile_app_v1.zip'",
67 },
68 {
69 id: "h8",
70 type: "file_create",
71 fileName: "Corporate Training Videos and Educational Content Series - Complete Collection.mp4",
72 userEmail: "[email protected]",
73 timestamp: "2024-01-13T14:15:00Z",
74 },
75 {
76 id: "h9",
77 type: "dir_create",
78 fileName: "Video Content and Multimedia Projects",
79 userEmail: "[email protected]",
80 timestamp: "2024-01-13T14:00:00Z",
81 },
82 {
83 id: "h10",
84 type: "file_remove",
85 fileName: "temp_presentation.pptx",
86 userEmail: "[email protected]",
87 timestamp: "2024-01-13T11:30:00Z",
88 },
89 {
90 id: "h11",
91 type: "file_create",
92 fileName: "Professional Headshots and Corporate Event Photography - High Resolution.png",
93 userEmail: "[email protected]",
94 timestamp: "2024-01-13T11:15:00Z",
95 },
96 {
97 id: "h12",
98 type: "rename",
99 fileName: "Travel and Vacation Photos Collection",
100 userEmail: "[email protected]",
101 timestamp: "2024-01-12T20:50:00Z",
102 details: "Renamed from 'Vacation Photos'",
103 },
104 {
105 id: "h13",
106 type: "dir_create",
107 fileName: "Photography and Visual Content",
108 userEmail: "[email protected]",
109 timestamp: "2024-01-12T16:00:00Z",
110 },
111 {
112 id: "h14",
113 type: "file_create",
114 fileName: "Professional Resume and Cover Letter Templates - Updated 2024.docx",
115 userEmail: "[email protected]",
116 timestamp: "2024-01-12T12:00:00Z",
117 },
118 {
119 id: "h15",
120 type: "file_remove",
121 fileName: "draft_document.docx",
122 userEmail: "[email protected]",
123 timestamp: "2024-01-11T16:20:00Z",
124 },
125]
126 10
127function formatTimestamp(timestamp: string): string { 11function formatTimestamp(timestamp: number): string {
128 const date = new Date(timestamp) 12 const date = new Date(timestamp * 1000) // Convert Unix timestamp to milliseconds
129 return date.toLocaleString("en-US", { 13 return date.toLocaleString("en-US", {
130 year: "numeric", 14 year: "numeric",
131 month: "short", 15 month: "short",
@@ -136,49 +20,106 @@ function formatTimestamp(timestamp: string): string {
136 }) 20 })
137} 21}
138 22
139function getActionIcon(type: HistoryEntry["type"]) { 23function getActionIcon(action: string) {
140 switch (type) { 24 switch (action) {
141 case "file_create": 25 case "create_file":
142 return <FileText className="h-4 w-4 text-green-600" /> 26 return <FileText className="h-4 w-4 text-green-600" />
143 case "dir_create": 27 case "create_dir":
144 return <Folder className="h-4 w-4 text-blue-600" /> 28 return <Folder className="h-4 w-4 text-blue-600" />
145 case "file_remove": 29 case "remove":
146 return <Trash2 className="h-4 w-4 text-red-600" /> 30 return <Trash2 className="h-4 w-4 text-red-600" />
147 case "rename": 31 default:
148 return <Edit className="h-4 w-4 text-orange-600" /> 32 return <Plus className="h-4 w-4 text-gray-600" />
149 } 33 }
150} 34}
151 35
152function getActionBadge(type: HistoryEntry["type"]) { 36function getActionBadge(action: string) {
153 switch (type) { 37 switch (action) {
154 case "file_create": 38 case "create_file":
155 return ( 39 return (
156 <Badge variant="outline" className="text-green-700 border-green-300 bg-green-50"> 40 <Badge variant="outline" className="text-green-700 border-green-300 bg-green-50">
157 File Created 41 File Created
158 </Badge> 42 </Badge>
159 ) 43 )
160 case "dir_create": 44 case "create_dir":
161 return ( 45 return (
162 <Badge variant="outline" className="text-blue-700 border-blue-300 bg-blue-50"> 46 <Badge variant="outline" className="text-blue-700 border-blue-300 bg-blue-50">
163 Directory Created 47 Directory Created
164 </Badge> 48 </Badge>
165 ) 49 )
166 case "file_remove": 50 case "remove":
167 return ( 51 return (
168 <Badge variant="outline" className="text-red-700 border-red-300 bg-red-50"> 52 <Badge variant="outline" className="text-red-700 border-red-300 bg-red-50">
169 File Removed 53 File/Directory Removed
170 </Badge> 54 </Badge>
171 ) 55 )
172 case "rename": 56 default:
173 return ( 57 return (
174 <Badge variant="outline" className="text-orange-700 border-orange-300 bg-orange-50"> 58 <Badge variant="outline" className="text-gray-700 border-gray-300 bg-gray-50">
175 Renamed 59 {action}
176 </Badge> 60 </Badge>
177 ) 61 )
178 } 62 }
179} 63}
180 64
181export default function HistoryView() { 65export default function HistoryView() {
66 const [logEntries, setLogEntries] = useState<DriveLogEntry[]>([])
67 const [loading, setLoading] = useState(true)
68 const [error, setError] = useState<string | null>(null)
69
70 useEffect(() => {
71 async function fetchLogEntries() {
72 try {
73 setLoading(true)
74 const response = await fetch('/api/log')
75 if (!response.ok) {
76 throw new Error('Failed to fetch log entries')
77 }
78 const data: DriveLogEntry[] = await response.json()
79 // Reverse to show latest entries first
80 setLogEntries(data.reverse())
81 } catch (err) {
82 setError(err instanceof Error ? err.message : 'Unknown error')
83 } finally {
84 setLoading(false)
85 }
86 }
87
88 fetchLogEntries()
89 }, [])
90
91 if (loading) {
92 return (
93 <div className="space-y-6">
94 <div className="flex items-center justify-between">
95 <div>
96 <h2 className="text-xl font-semibold">Activity History</h2>
97 <p className="text-sm text-muted-foreground">Recent filesystem modifications and changes</p>
98 </div>
99 </div>
100 <div className="text-center py-8">
101 <p>Loading history...</p>
102 </div>
103 </div>
104 )
105 }
106
107 if (error) {
108 return (
109 <div className="space-y-6">
110 <div className="flex items-center justify-between">
111 <div>
112 <h2 className="text-xl font-semibold">Activity History</h2>
113 <p className="text-sm text-muted-foreground">Recent filesystem modifications and changes</p>
114 </div>
115 </div>
116 <div className="text-center py-8 text-red-600">
117 <p>Error: {error}</p>
118 </div>
119 </div>
120 )
121 }
122
182 return ( 123 return (
183 <div className="space-y-6"> 124 <div className="space-y-6">
184 {/* History Header */} 125 {/* History Header */}
@@ -187,7 +128,7 @@ export default function HistoryView() {
187 <h2 className="text-xl font-semibold">Activity History</h2> 128 <h2 className="text-xl font-semibold">Activity History</h2>
188 <p className="text-sm text-muted-foreground">Recent filesystem modifications and changes</p> 129 <p className="text-sm text-muted-foreground">Recent filesystem modifications and changes</p>
189 </div> 130 </div>
190 <Badge variant="secondary">{mockHistoryData.length} total entries</Badge> 131 <Badge variant="secondary">{logEntries.length} total entries</Badge>
191 </div> 132 </div>
192 133
193 {/* History Table */} 134 {/* History Table */}
@@ -200,25 +141,27 @@ export default function HistoryView() {
200 <TableHead>File/Directory Name</TableHead> 141 <TableHead>File/Directory Name</TableHead>
201 <TableHead>User</TableHead> 142 <TableHead>User</TableHead>
202 <TableHead>Timestamp</TableHead> 143 <TableHead>Timestamp</TableHead>
203 <TableHead>Details</TableHead> 144 <TableHead>Size</TableHead>
204 </TableRow> 145 </TableRow>
205 </TableHeader> 146 </TableHeader>
206 <TableBody> 147 <TableBody>
207 {mockHistoryData.map((entry) => ( 148 {logEntries.map((entry) => (
208 <TableRow key={entry.id} className="hover:bg-muted/50"> 149 <TableRow key={`${entry.log_id}`} className="hover:bg-muted/50">
209 <TableCell>{getActionIcon(entry.type)}</TableCell> 150 <TableCell>{getActionIcon(entry.action)}</TableCell>
210 <TableCell>{getActionBadge(entry.type)}</TableCell> 151 <TableCell>{getActionBadge(entry.action)}</TableCell>
211 <TableCell className="font-medium"> 152 <TableCell className="font-medium">
212 <span className="break-words">{entry.fileName}</span> 153 <span className="break-words">{entry.path}</span>
213 </TableCell> 154 </TableCell>
214 <TableCell> 155 <TableCell>
215 <span className="text-sm font-mono">{entry.userEmail}</span> 156 <span className="text-sm font-mono">{entry.email}</span>
216 </TableCell> 157 </TableCell>
217 <TableCell> 158 <TableCell>
218 <span className="text-sm">{formatTimestamp(entry.timestamp)}</span> 159 <span className="text-sm">{formatTimestamp(entry.timestamp)}</span>
219 </TableCell> 160 </TableCell>
220 <TableCell> 161 <TableCell>
221 <span className="text-sm text-muted-foreground">{entry.details || "—"}</span> 162 <span className="text-sm text-muted-foreground">
163 {entry.size ? formatFileSize(entry.size) : "—"}
164 </span>
222 </TableCell> 165 </TableCell>
223 </TableRow> 166 </TableRow>
224 ))} 167 ))}
diff --git a/frontend/lib/utils.ts b/frontend/lib/utils.ts
index bd0c391..200e3e7 100644
--- a/frontend/lib/utils.ts
+++ b/frontend/lib/utils.ts
@@ -4,3 +4,11 @@ import { twMerge } from "tailwind-merge"
4export function cn(...inputs: ClassValue[]) { 4export function cn(...inputs: ClassValue[]) {
5 return twMerge(clsx(inputs)) 5 return twMerge(clsx(inputs))
6} 6}
7
8export function formatFileSize(bytes: number): string {
9 if (bytes === 0) return "0 Bytes"
10 const k = 1024
11 const sizes = ["Bytes", "KB", "MB", "GB"]
12 const i = Math.floor(Math.log(bytes) / Math.log(k))
13 return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
14}