diff --git a/frontend/src/components/asset/AssetBrowser.vue b/frontend/src/components/asset/AssetBrowser.vue index e4d2c1d..58caadf 100644 --- a/frontend/src/components/asset/AssetBrowser.vue +++ b/frontend/src/components/asset/AssetBrowser.vue @@ -161,26 +161,14 @@ - - - - Delete Asset - - Are you sure you want to delete "{{ selectedAsset?.name }}"? This - action cannot be undone and will remove all associated tasks. - - - - Cancel - - Delete Asset - - - - + (null); +const deletionInfo = ref(null); + const taskStatusFilter = ref('') // Thumbnail display state - with session storage @@ -563,9 +547,20 @@ const editAsset = (asset: Asset) => { showEditDialog.value = true; }; -const deleteAsset = (asset: Asset) => { - selectedAsset.value = asset; - showDeleteDialog.value = true; +const deleteAsset = async (asset: Asset) => { + // Don't set selectedAsset here as it opens the detail panel + assetToDelete.value = asset; + + try { + deletionInfo.value = await assetService.getAssetDeletionInfo(asset.id); + showDeleteDialog.value = true; + } catch (err) { + toast({ + title: "Failed to get asset information", + description: err instanceof Error ? err.message : "An error occurred", + variant: "destructive", + }); + } }; const viewAssetTasks = (asset: Asset) => { @@ -619,15 +614,21 @@ const handleUpdateAsset = async (assetData: AssetCreate | AssetUpdate) => { }; const handleDeleteAsset = async () => { - if (!selectedAsset.value) return; + if (!assetToDelete.value) return; try { - await assetsStore.deleteAsset(selectedAsset.value.id); + await assetsStore.deleteAsset(assetToDelete.value.id); + showDeleteDialog.value = false; - selectedAsset.value = null; + assetToDelete.value = null; + + const taskCount = deletionInfo.value?.task_count || 0; + deletionInfo.value = null; toast({ title: "Asset deleted", - description: "Asset has been deleted successfully.", + description: taskCount > 0 + ? `Asset and ${taskCount} associated task${taskCount === 1 ? '' : 's'} deleted successfully.` + : "Asset has been deleted successfully.", }); } catch (err) { toast({ diff --git a/frontend/src/components/asset/AssetDeleteConfirmDialog.vue b/frontend/src/components/asset/AssetDeleteConfirmDialog.vue index e15981e..9f3b877 100644 --- a/frontend/src/components/asset/AssetDeleteConfirmDialog.vue +++ b/frontend/src/components/asset/AssetDeleteConfirmDialog.vue @@ -21,13 +21,11 @@ -
-
- - Failed to load deletion information -
-

{{ loadError }}

-
+ + + Failed to load deletion information + {{ loadError }} +
@@ -73,59 +71,56 @@
-
-
- - - {{ deletionInfo.affected_users.length }} user{{ deletionInfo.affected_users.length === 1 ? '' : 's' }} will be affected - -
- -

- The following users have work associated with this asset that will be marked as deleted: -

+ + + + {{ deletionInfo.affected_users.length }} user{{ deletionInfo.affected_users.length === 1 ? '' : 's' }} will be affected + + +

+ The following users have work associated with this asset that will be marked as deleted: +

-
-
-
-
{{ user.name }}
-
{{ user.email }} ??{{ user.role }}
-
-
-
{{ user.task_count }} task{{ user.task_count === 1 ? '' : 's' }}
-
{{ user.submission_count }} submission{{ user.submission_count === 1 ? '' : 's' }}
-
{{ user.note_count }} note{{ user.note_count === 1 ? '' : 's' }}
-
- Last active: {{ formatDate(user.last_activity_date) }} +
+
+
+
{{ user.name }}
+
{{ user.email }} • {{ user.role }}
+
+
+
{{ user.task_count }} task{{ user.task_count === 1 ? '' : 's' }}
+
{{ user.submission_count }} submission{{ user.submission_count === 1 ? '' : 's' }}
+
{{ user.note_count }} note{{ user.note_count === 1 ? '' : 's' }}
+
+ Last active: {{ formatDate(user.last_activity_date) }} +
-
-
+ + -
-
- - No users will be affected by this deletion. -
-
+ + + + No users will be affected by this deletion. + + -
-
- - Data Preservation -
-

- All data will be preserved in the database and can be recovered by administrators. + + + Data Preservation + + All data will be preserved in the database and can be recovered by administrators. Files will remain on the server unchanged. This is a soft deletion, not permanent removal. -

-
+ +
@@ -189,6 +184,11 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog' +import { + Alert, + AlertDescription, + AlertTitle, +} from '@/components/ui/alert' import { assetService, type AssetDeletionInfo } from '@/services/asset' import { useToast } from '@/components/ui/toast/use-toast' @@ -275,7 +275,7 @@ watch(() => props.open, (newOpen) => { deletionInfo.value = null loadError.value = null } -}) +}, { immediate: true }) const handleDelete = async () => { if (!isConfirmed.value) return diff --git a/frontend/src/components/asset/EditableTaskStatus.vue b/frontend/src/components/asset/EditableTaskStatus.vue index c4566ad..8bfac0c 100644 --- a/frontend/src/components/asset/EditableTaskStatus.vue +++ b/frontend/src/components/asset/EditableTaskStatus.vue @@ -10,14 +10,9 @@ - - - - - - + /> [ { title: 'API Keys', url: '/developer/api-keys', icon: Key }, { title: 'All Projects', url: '/developer/projects', icon: Database }, { title: 'All Tasks', url: '/developer/tasks', icon: CheckSquare }, - { title: 'Usage Analytics', url: '/developer/analytics', icon: BarChart3 }, - { title: 'Documentation', url: '/developer/docs', icon: FileText } + { title: 'Usage Analytics', url: '/developer/analytics', icon: BarChart3 } ]) // Mock recent projects - this would come from a store in real implementation diff --git a/frontend/src/components/layout/UserMenu.vue b/frontend/src/components/layout/UserMenu.vue index 53d5d59..bc21b6c 100644 --- a/frontend/src/components/layout/UserMenu.vue +++ b/frontend/src/components/layout/UserMenu.vue @@ -65,26 +65,11 @@ Profile - - - Preferences - - - - Notifications - - {{ notificationsEnabled ? 'On' : 'Off' }} - - - + - - - Help & Support - Keyboard Shortcuts @@ -99,6 +84,24 @@ + + + + + Keyboard Shortcuts + +
+
+ Toggle sidebar + ⌘/Ctrl + B +
+
+ Quick search + ⌘/Ctrl + K +
+
+
+
@@ -111,11 +114,8 @@ import { LogOut, User, Settings, - Bell, Key, Users, - Palette, - HelpCircle, Keyboard, } from 'lucide-vue-next' import { @@ -132,6 +132,12 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' import { SidebarMenu, SidebarMenuButton, @@ -181,8 +187,7 @@ const showRoleFeatures = computed(() => { return user.value?.role === 'developer' || isAdminOrCoordinator.value || user.value?.is_admin }) -// Notifications state (this would typically come from a notifications store) -const notificationsEnabled = ref(true) +const showShortcutsDialog = ref(false) // Actions const navigateTo = (path: string) => { @@ -198,24 +203,8 @@ const handleLogout = async () => { } } -const toggleNotifications = () => { - notificationsEnabled.value = !notificationsEnabled.value - // In a real app, this would update user preferences - console.log('Notifications toggled:', notificationsEnabled.value) -} - const showKeyboardShortcuts = () => { - // In a real app, this would open a modal with keyboard shortcuts - console.log('Keyboard shortcuts modal would open here') - // For now, just show an alert with some common shortcuts - alert(`Keyboard Shortcuts: - -⌘/Ctrl + B - Toggle sidebar -⌘/Ctrl + K - Quick search -⌘/Ctrl + , - Open preferences -⌘/Ctrl + / - Show help - -More shortcuts available in the help documentation.`) + showShortcutsDialog.value = true } diff --git a/frontend/src/components/project/ProjectTabs.vue b/frontend/src/components/project/ProjectTabs.vue index 36d3518..10c8343 100644 --- a/frontend/src/components/project/ProjectTabs.vue +++ b/frontend/src/components/project/ProjectTabs.vue @@ -102,49 +102,31 @@ const tabs = computed(() => [ // Determine active tab based on current route const activeTab = computed(() => { const currentPath = route.path; - - // Debug logging - console.log('ProjectTabs - Current path:', currentPath); - console.log('ProjectTabs - Project ID:', props.projectId); if (currentPath === `/projects/${props.projectId}`) { - console.log('ProjectTabs - Active tab: overview'); return "overview"; } else if (currentPath.startsWith(`/projects/${props.projectId}/shots`)) { - console.log('ProjectTabs - Active tab: shots'); return "shots"; } else if (currentPath.startsWith(`/projects/${props.projectId}/assets`)) { - console.log('ProjectTabs - Active tab: assets'); return "assets"; } else if (currentPath.startsWith(`/projects/${props.projectId}/tasks`)) { - console.log('ProjectTabs - Active tab: tasks'); return "tasks"; } else if ( currentPath.startsWith(`/projects/${props.projectId}/settings`) ) { - console.log('ProjectTabs - Active tab: settings'); return "settings"; } - console.log('ProjectTabs - Active tab: overview (default)'); return "overview"; }); // Set active tab and navigate const setActiveTab = (tabId: string) => { const tab = tabs.value.find((t) => t.id === tabId); - console.log('ProjectTabs - Setting active tab:', tabId); - console.log('ProjectTabs - Tab found:', tab); - console.log('ProjectTabs - Current route path:', route.path); - console.log('ProjectTabs - Target route:', tab?.route); - if (tab) { - console.log('ProjectTabs - Navigating to:', tab.route); - router.push(tab.route).catch(err => { - console.error('ProjectTabs - Navigation error:', err); + router.push(tab.route).catch(() => { + // Navigation aborted (e.g. duplicate route) — safe to ignore }); - } else { - console.log('ProjectTabs - Tab not found'); } }; diff --git a/frontend/src/components/settings/CustomTaskTypeManager.vue b/frontend/src/components/settings/CustomTaskTypeManager.vue index ed8c705..ccf1363 100644 --- a/frontend/src/components/settings/CustomTaskTypeManager.vue +++ b/frontend/src/components/settings/CustomTaskTypeManager.vue @@ -428,21 +428,13 @@ const handleDialogSave = async () => { } const handleDelete = (category: 'asset' | 'shot', taskType: string) => { - console.log('=== HANDLE DELETE ===') - console.log('Received taskType:', taskType) - console.log('Received category:', category) - taskTypeToDelete.value = taskType categoryToDelete.value = category deleteError.value = '' isDeleteDialogOpen.value = true - - console.log('Set taskTypeToDelete.value to:', taskTypeToDelete.value) - console.log('Set categoryToDelete.value to:', categoryToDelete.value) } const closeDeleteDialog = () => { - console.log('=== CLOSE DELETE DIALOG ===') isDeleteDialogOpen.value = false // Values will be cleared by @update:open handler } @@ -451,35 +443,22 @@ const confirmDelete = async () => { // Capture values immediately before any async operations const taskTypeToDeleteLocal = taskTypeToDelete.value const categoryToDeleteLocal = categoryToDelete.value - + try { isDeleting.value = true deleteError.value = '' - - console.log('=== DELETE DEBUG ===') - console.log('taskTypeToDelete.value:', taskTypeToDeleteLocal) - console.log('categoryToDelete.value:', categoryToDeleteLocal) - console.log('projectId:', props.projectId) - + if (!taskTypeToDeleteLocal) { - console.error('ERROR: taskTypeToDelete is empty!') deleteError.value = 'Task type name is missing. Please try again.' isDeleting.value = false return } - - console.log('Deleting task type:', { - projectId: props.projectId, - taskType: taskTypeToDeleteLocal, - category: categoryToDeleteLocal - }) - + const response = await customTaskTypeService.deleteCustomTaskType( props.projectId, taskTypeToDeleteLocal, categoryToDeleteLocal ) - console.log('Delete task type response:', response) taskTypes.value = response toast({ diff --git a/frontend/src/components/shot/ShotDeleteConfirmDialog.vue b/frontend/src/components/shot/ShotDeleteConfirmDialog.vue index ad74aa4..22fbcfc 100644 --- a/frontend/src/components/shot/ShotDeleteConfirmDialog.vue +++ b/frontend/src/components/shot/ShotDeleteConfirmDialog.vue @@ -258,7 +258,7 @@ watch(() => props.open, (newOpen) => { deletionInfo.value = null loadError.value = null } -}) +}, { immediate: true }) const handleDelete = async () => { if (!isConfirmed.value) return diff --git a/frontend/src/components/task/NoteItem.vue b/frontend/src/components/task/NoteItem.vue index 741f8d6..e3e3cbe 100644 --- a/frontend/src/components/task/NoteItem.vue +++ b/frontend/src/components/task/NoteItem.vue @@ -93,6 +93,23 @@
+ + + + + Delete Note + + Are you sure you want to delete this note? This action cannot be undone. + + + + Cancel + + Delete + + + +
@@ -102,6 +119,16 @@ import { Reply, Pencil, Trash2 } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' import { taskService, type ProductionNote } from '@/services/task' import { useAuthStore } from '@/stores/auth' import { useToast } from '@/components/ui/toast/use-toast' @@ -121,6 +148,7 @@ const authStore = useAuthStore() const editing = ref(false) const editContent = ref('') +const showDeleteDialog = ref(false) const canEdit = computed(() => { return authStore.user?.id === props.note.user_id || authStore.user?.is_admin @@ -174,9 +202,11 @@ async function handleSave() { } } -async function handleDelete() { - if (!confirm('Are you sure you want to delete this note?')) return +function handleDelete() { + showDeleteDialog.value = true +} +async function confirmDelete() { try { await taskService.deleteTaskNote(props.taskId, props.note.id) emit('noteUpdated') @@ -185,12 +215,13 @@ async function handleDelete() { description: 'Note deleted successfully' }) } catch (error: any) { - console.error('Error deleting note:', error) toast({ title: 'Error', description: error.response?.data?.detail || 'Failed to delete note', variant: 'destructive' }) + } finally { + showDeleteDialog.value = false } } diff --git a/frontend/src/components/task/TaskAttachments.vue b/frontend/src/components/task/TaskAttachments.vue index 3e08186..406f9f1 100644 --- a/frontend/src/components/task/TaskAttachments.vue +++ b/frontend/src/components/task/TaskAttachments.vue @@ -104,6 +104,23 @@ + + + + + Delete Attachment + + Are you sure you want to delete this attachment? This action cannot be undone. + + + + Cancel + + Delete + + + + @@ -125,6 +142,16 @@ import { DialogTitle, DialogDescription, } from '@/components/ui/dialog' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' import AttachmentCard from './AttachmentCard.vue' import { taskService, type TaskAttachment } from '@/services/task' import { useToast } from '@/components/ui/toast/use-toast' @@ -148,6 +175,8 @@ const filterType = ref('all') const viewerOpen = ref(false) const selectedAttachment = ref(null) const mediaBlobUrl = ref(null) +const showDeleteDialog = ref(false) +const attachmentToDelete = ref(null) const attachmentTypes = [ { value: 'all', label: 'All' }, @@ -197,23 +226,30 @@ async function handleFileSelect(event: Event) { } } -async function handleDelete(attachmentId: number) { - if (!confirm('Are you sure you want to delete this attachment?')) return +function handleDelete(attachmentId: number) { + attachmentToDelete.value = attachmentId + showDeleteDialog.value = true +} + +async function confirmDeleteAttachment() { + if (attachmentToDelete.value === null) return try { - await taskService.deleteTaskAttachment(props.taskId, attachmentId) + await taskService.deleteTaskAttachment(props.taskId, attachmentToDelete.value) emit('attachmentsUpdated') toast({ title: 'Success', description: 'Attachment deleted successfully' }) } catch (error: any) { - console.error('Error deleting attachment:', error) toast({ title: 'Error', description: error.response?.data?.detail || 'Failed to delete attachment', variant: 'destructive' }) + } finally { + showDeleteDialog.value = false + attachmentToDelete.value = null } } diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 25c19ae..2cb4150 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -83,6 +83,12 @@ const routes: RouteRecordRaw[] = [ name: 'ProjectSettings', component: () => import('@/views/ProjectSettingsView.vue'), meta: { tab: 'settings', tabLabel: 'Settings' } + }, + { + path: 'technical-specs', + name: 'ProjectTechnicalSpecs', + component: () => import('@/views/ProjectTechnicalSpecsView.vue'), + meta: { tab: 'technical-specs', tabLabel: 'Technical Specs' } } ] }, diff --git a/frontend/src/services/asset.ts b/frontend/src/services/asset.ts index 1b84cc2..3b739bd 100644 --- a/frontend/src/services/asset.ts +++ b/frontend/src/services/asset.ts @@ -150,18 +150,8 @@ class AssetService { } const url = `/assets/?${params}` - console.log('AssetService - Fetching assets from:', url) - console.log('AssetService - Project ID:', projectId) - - try { - const response = await apiClient.get(url) - console.log('AssetService - Assets response status:', response.status) - console.log('AssetService - Assets response data:', response.data) - return response.data - } catch (error) { - console.error('AssetService - Error fetching assets:', error) - throw error - } + const response = await apiClient.get(url) + return response.data } async getAsset(assetId: number): Promise { diff --git a/frontend/src/services/developer.ts b/frontend/src/services/developer.ts new file mode 100644 index 0000000..ac50986 --- /dev/null +++ b/frontend/src/services/developer.ts @@ -0,0 +1,15 @@ +import { apiClient } from './api' + +export interface DeveloperStats { + total_projects: number + total_tasks: number + total_submissions: number + api_usage_count: number +} + +export const developerService = { + async getStats(): Promise { + const response = await apiClient.get('/developer/stats') + return response.data + } +} diff --git a/frontend/src/services/review.ts b/frontend/src/services/review.ts new file mode 100644 index 0000000..dd7537b --- /dev/null +++ b/frontend/src/services/review.ts @@ -0,0 +1,20 @@ +import { apiClient } from './api' + +export interface PendingReviewSubmission { + id: number + task_id: number + user_id: number + file_name: string + version_number: number + submitted_at: string + user_first_name: string + user_last_name: string +} + +export const reviewService = { + async getPendingReviews(projectId?: number): Promise { + const params = projectId ? { project_id: projectId } : {} + const response = await apiClient.get('/reviews/pending', { params }) + return response.data + } +} diff --git a/frontend/src/services/user.ts b/frontend/src/services/user.ts index 580cf53..4813da9 100644 --- a/frontend/src/services/user.ts +++ b/frontend/src/services/user.ts @@ -86,17 +86,8 @@ export const userService = { is_approved?: boolean is_admin?: boolean }): Promise { - console.log('userService.editUser called with:', { userId, userData }) - try { - const response = await apiClient.put(`/users/${userId}`, userData) - return normalizeUser(response.data) - } catch (error: any) { - console.error('editUser API error:', error.response?.data) - if (error.response?.data?.detail && Array.isArray(error.response.data.detail)) { - console.error('Validation errors:', JSON.stringify(error.response.data.detail, null, 2)) - } - throw error - } + const response = await apiClient.put(`/users/${userId}`, userData) + return normalizeUser(response.data) }, async resetUserPassword(userId: number, newPassword: string): Promise<{ message: string; user_id: number }> { diff --git a/frontend/src/views/DashboardView.vue b/frontend/src/views/DashboardView.vue index 81f10e6..ca5111a 100644 --- a/frontend/src/views/DashboardView.vue +++ b/frontend/src/views/DashboardView.vue @@ -295,8 +295,8 @@
-

{{ submission.task_name }}

-

by {{ submission.artist_name }}

+

{{ submission.file_name }}

+

by {{ submission.user_first_name }} {{ submission.user_last_name }}

{{ formatSubmissionTime(submission.submitted_at) }}
@@ -341,10 +341,10 @@
-

{{ activity.action }}

+

{{ formatActivityType(activity.type) }}

{{ activity.description }}

-
{{ formatTime(activity.timestamp) }}
+
{{ formatTime(activity.created_at) }}
@@ -400,10 +400,6 @@ Manage API Keys - @@ -428,43 +424,57 @@ import { computed, ref, onMounted } from 'vue' import { useRouter } from 'vue-router' import { - CheckSquare, Clock, CheckCircle, FolderOpen, User, AlertTriangle, Users, - TrendingUp, Eye, RotateCcw, Key, Activity, Database, UserCheck, Shield, - FileText + CheckSquare, Clock, CheckCircle, FolderOpen, User, AlertTriangle, Users, + TrendingUp, Eye, RotateCcw, Key, Activity, Database, UserCheck, Shield } from 'lucide-vue-next' import { Button } from '@/components/ui/button' import { useAuthStore } from '@/stores/auth' +import { useProjectsStore } from '@/stores/projects' +import { useTasksStore } from '@/stores/tasks' +import { userService } from '@/services/user' +import { apiKeyService } from '@/services/apiKey' +import { developerService } from '@/services/developer' +import { reviewService, type PendingReviewSubmission } from '@/services/review' +import { getRecentActivities } from '@/services/activity' +import type { Activity as ActivityRecord } from '@/types/activity' +import { ActivityType } from '@/types/activity' const authStore = useAuthStore() const router = useRouter() +const projectsStore = useProjectsStore() +const tasksStore = useTasksStore() const user = computed(() => authStore.user) const userRole = computed(() => authStore.userRole) const isAdmin = computed(() => authStore.isAdmin) -// Dashboard stats (mock data for now - would be fetched from API) const dashboardStats = ref({ // Artist stats + // TODO(backend): "my tasks/projects across all projects" has no dedicated endpoint yet activeTasks: 0, pendingReviews: 0, completedTasks: 0, myProjects: 0, - + // Coordinator stats totalProjects: 0, overdueTasks: 0, + // TODO(backend): no endpoint for role-filtered active-artist counts activeArtists: 0, + // TODO(backend): Project has no completion_rate field completionRate: 0, - + // Director stats + // TODO(backend): no "approved/retakes today" aggregate endpoint approvedToday: 0, retakesRequested: 0, - + // Developer stats apiKeys: 0, + // TODO(backend): /developer/stats' api_usage_count is all-time, not "today" apiCallsToday: 0, totalTasks: 0, - + // Admin stats totalUsers: 0, pendingApprovals: 0, @@ -473,23 +483,12 @@ const dashboardStats = ref({ // Role-specific data const recentTasks = ref([]) +// TODO(backend): project completion_rate isn't available yet, so this list is left empty for now const projectOverview = ref([]) -const pendingReviews = ref([]) +const pendingReviews = ref([]) +// TODO(backend): needs a frontend wrapper for GET /developer/api-usage plus per-day filtering const apiActivity = ref([]) -const systemActivity = ref([ - { - id: 1, - action: 'User Registration', - description: 'New user registered and awaiting approval', - timestamp: new Date().toISOString() - }, - { - id: 2, - action: 'Project Created', - description: 'New project "Animation Series" created', - timestamp: new Date(Date.now() - 3600000).toISOString() - } -]) +const systemActivity = ref([]) // Methods const formatRole = (role?: string) => { @@ -535,6 +534,13 @@ const formatTime = (timestamp: string) => { return `${diffDays}d ago` } +const formatActivityType = (type: ActivityType) => { + return type + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' ') +} + const getTaskStatusColor = (status: string) => { switch (status) { case 'not_started': return 'bg-gray-500' @@ -562,26 +568,59 @@ const navigateTo = (path: string) => { } const loadDashboardData = async () => { - // Mock data loading - in real implementation, this would fetch from API based on user role try { - // Load role-specific stats - dashboardStats.value = { - activeTasks: 5, - pendingReviews: 2, - completedTasks: 28, - myProjects: 3, - totalProjects: 8, - overdueTasks: 3, - activeArtists: 12, - completionRate: 75, - approvedToday: 8, - retakesRequested: 2, - apiKeys: 3, - apiCallsToday: 156, - totalTasks: 245, - totalUsers: 25, - pendingApprovals: 2, - activeProjects: 5 + const needsProjects = userRole.value === 'coordinator' || userRole.value === 'director' || isAdmin.value + const tasks: Promise[] = [] + + if (needsProjects) { + tasks.push(projectsStore.fetchProjects()) + } + if (userRole.value === 'coordinator') { + tasks.push(tasksStore.fetchTasks()) + } + if (userRole.value === 'director') { + tasks.push( + reviewService.getPendingReviews().then(submissions => { + pendingReviews.value = submissions + }) + ) + } + if (userRole.value === 'developer') { + tasks.push( + apiKeyService.getAPIKeys().then(keys => { + dashboardStats.value.apiKeys = keys.length + }), + developerService.getStats().then(stats => { + dashboardStats.value.totalProjects = stats.total_projects + dashboardStats.value.totalTasks = stats.total_tasks + }) + ) + } + if (isAdmin.value) { + tasks.push( + userService.getUsers().then(users => { + dashboardStats.value.totalUsers = users.length + }), + userService.getPendingUsers().then(users => { + dashboardStats.value.pendingApprovals = users.length + }), + getRecentActivities().then(activities => { + systemActivity.value = activities + }) + ) + } + + await Promise.all(tasks) + + if (needsProjects) { + dashboardStats.value.totalProjects = projectsStore.projects.length + dashboardStats.value.activeProjects = projectsStore.projectsInProgress.length + } + if (userRole.value === 'coordinator') { + dashboardStats.value.overdueTasks = tasksStore.overdueTasks.length + } + if (userRole.value === 'director') { + dashboardStats.value.pendingReviews = pendingReviews.value.length } } catch (error) { console.error('Failed to load dashboard data:', error) diff --git a/frontend/src/views/UsersView.vue b/frontend/src/views/UsersView.vue index 3af4fee..39e8446 100644 --- a/frontend/src/views/UsersView.vue +++ b/frontend/src/views/UsersView.vue @@ -257,15 +257,11 @@ const handleEditUser = (user: User) => { const handleEditUserSubmit = async (userId: number, data: UserEditData) => { try { isEditingUser.value = true - console.log('Editing user with data:', data) await userStore.editUser(userId, data) showEditDialog.value = false showSuccessMessage('User updated successfully') await refreshData() } catch (err: any) { - console.error('Failed to edit user:', err) - console.error('Error response:', err.response?.data) - // Handle FastAPI validation errors (422) let errorMessage = 'Failed to update user' if (err.response?.data?.detail) { @@ -276,7 +272,6 @@ const handleEditUserSubmit = async (userId: number, data: UserEditData) => { return `${field}: ${e.msg}` }).join(', ') errorMessage = errors - console.error('Validation errors:', err.response.data.detail) } else { errorMessage = err.response.data.detail } diff --git a/frontend/src/views/admin/DeletedItemsManagementView.vue b/frontend/src/views/admin/DeletedItemsManagementView.vue index 5529ac8..18158e4 100644 --- a/frontend/src/views/admin/DeletedItemsManagementView.vue +++ b/frontend/src/views/admin/DeletedItemsManagementView.vue @@ -915,26 +915,18 @@ const loadDeletedItems = async () => { const projectId = selectedProjectId.value && selectedProjectId.value !== 'all' ? parseInt(selectedProjectId.value) : undefined - console.log('Loading recovery data with projectId:', projectId) - const [shots, assets] = await Promise.all([ recoveryService.getDeletedShots(projectId), recoveryService.getDeletedAssets(projectId) ]) - console.log('Loaded shots for recovery:', shots.length) - console.log('Loaded assets for recovery:', assets.length) - console.log('Shots data:', shots) - deletedShots.value = shots deletedAssets.value = assets - + // Clear selections when data changes selectedItems.value = [] } catch (err: any) { error.value = err.response?.data?.detail || 'Failed to load recovery data' - console.error('Failed to load recovery data:', err) - console.error('Error details:', err.response) } finally { isLoading.value = false } diff --git a/frontend/src/views/auth/LoginView.vue b/frontend/src/views/auth/LoginView.vue index ad0f7c1..5957033 100644 --- a/frontend/src/views/auth/LoginView.vue +++ b/frontend/src/views/auth/LoginView.vue @@ -43,16 +43,6 @@ Login - -
{{ error }}
@@ -103,8 +93,4 @@ const handleSubmit = async () => { } } -const handleGoogleLogin = () => { - // TODO: Implement Google OAuth login - console.log('Google login not implemented yet') -} \ No newline at end of file diff --git a/frontend/src/views/auth/RegisterView.vue b/frontend/src/views/auth/RegisterView.vue index 038372f..a307fa1 100644 --- a/frontend/src/views/auth/RegisterView.vue +++ b/frontend/src/views/auth/RegisterView.vue @@ -73,16 +73,6 @@ Create account - -
{{ error }}
@@ -217,8 +207,4 @@ const handleSubmit = async () => { } } -const handleGoogleSignup = () => { - // TODO: Implement Google OAuth signup - console.log('Google signup not implemented yet') -} \ No newline at end of file diff --git a/frontend/src/views/developer/APIKeysView.vue b/frontend/src/views/developer/APIKeysView.vue index f0945db..ed3348c 100644 --- a/frontend/src/views/developer/APIKeysView.vue +++ b/frontend/src/views/developer/APIKeysView.vue @@ -11,8 +11,6 @@ :icon="Key" title="No API keys" description="Create API keys to integrate external applications with the VFX system." - action-text="Create API Key" - @action="() => {}" /> diff --git a/frontend/src/views/project/ProjectAssetsView.vue b/frontend/src/views/project/ProjectAssetsView.vue index a3d570d..eb2cb11 100644 --- a/frontend/src/views/project/ProjectAssetsView.vue +++ b/frontend/src/views/project/ProjectAssetsView.vue @@ -37,19 +37,8 @@ const assetsStore = useAssetsStore() // Computed properties const projectId = computed(() => { const id = route.params.projectId - const parsedId = typeof id === 'string' ? parseInt(id) : Array.isArray(id) ? parseInt(id[0]) : 0 - console.log('ProjectAssetsView - Route params:', route.params) - console.log('ProjectAssetsView - Raw project ID:', id) - console.log('ProjectAssetsView - Parsed project ID:', parsedId) - return parsedId + return typeof id === 'string' ? parseInt(id) : Array.isArray(id) ? parseInt(id[0]) : 0 }) -const totalAssets = computed(() => { - console.log('ProjectAssetsView - Total assets:', assetsStore.assets.length) - return assetsStore.assets.length -}) - -// Debug logging on mount -console.log('ProjectAssetsView - Component mounted') -console.log('ProjectAssetsView - Route params:', route.params) +const totalAssets = computed(() => assetsStore.assets.length) \ No newline at end of file diff --git a/frontend/src/views/project/ProjectOverviewView.vue b/frontend/src/views/project/ProjectOverviewView.vue index 6ec79fd..1b31cfc 100644 --- a/frontend/src/views/project/ProjectOverviewView.vue +++ b/frontend/src/views/project/ProjectOverviewView.vue @@ -105,29 +105,22 @@ - - - Recent Activity - - -
- -

Activity feed coming soon

-
-
-
+
+ +