Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/.eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,21 @@
"unref": true,
"useApi": true,
"useAttrs": true,
"useConfirm": true,
"useCreateProjectStore": true,
"useCssModule": true,
"useCssVars": true,
"useId": true,
"useLink": true,
"useModel": true,
"useProjectCreationComposable": true,
"useProjectListComposable": true,
"useProjectListStore": true,
"useRoute": true,
"useRouter": true,
"useSlots": true,
"useTemplateRef": true,
"useToast": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
Expand Down
8 changes: 8 additions & 0 deletions frontend/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ declare global {
const unref: (typeof import('vue'))['unref']
const useApi: (typeof import('./src/plugins/api'))['useApi']
const useAttrs: (typeof import('vue'))['useAttrs']
const useConfirm: (typeof import('primevue/useconfirm'))['useConfirm']
const useCreateProjectStore: (typeof import('./src/stores/create-project.store'))['useCreateProjectStore']
const useCssModule: (typeof import('vue'))['useCssModule']
const useCssVars: (typeof import('vue'))['useCssVars']
Expand All @@ -80,10 +81,13 @@ declare global {
const useLink: (typeof import('vue-router'))['useLink']
const useModel: (typeof import('vue'))['useModel']
const useProjectCreationComposable: (typeof import('./src/composables/useProjectCreationComposable'))['useProjectCreationComposable']
const useProjectListComposable: (typeof import('./src/composables/useProjectListComposable'))['useProjectListComposable']
const useProjectListStore: (typeof import('./src/stores/project-list.store'))['useProjectListStore']
const useRoute: (typeof import('vue-router'))['useRoute']
const useRouter: (typeof import('vue-router'))['useRouter']
const useSlots: (typeof import('vue'))['useSlots']
const useTemplateRef: (typeof import('vue'))['useTemplateRef']
const useToast: (typeof import('primevue/usetoast'))['useToast']
const watch: (typeof import('vue'))['watch']
const watchEffect: (typeof import('vue'))['watchEffect']
const watchPostEffect: (typeof import('vue'))['watchPostEffect']
Expand Down Expand Up @@ -172,17 +176,21 @@ declare module 'vue' {
readonly unref: UnwrapRef<(typeof import('vue'))['unref']>
readonly useApi: UnwrapRef<(typeof import('./src/plugins/api'))['useApi']>
readonly useAttrs: UnwrapRef<(typeof import('vue'))['useAttrs']>
readonly useConfirm: UnwrapRef<(typeof import('primevue/useconfirm'))['useConfirm']>
readonly useCreateProjectStore: UnwrapRef<(typeof import('./src/stores/create-project.store'))['useCreateProjectStore']>
readonly useCssModule: UnwrapRef<(typeof import('vue'))['useCssModule']>
readonly useCssVars: UnwrapRef<(typeof import('vue'))['useCssVars']>
readonly useId: UnwrapRef<(typeof import('vue'))['useId']>
readonly useLink: UnwrapRef<(typeof import('vue-router'))['useLink']>
readonly useModel: UnwrapRef<(typeof import('vue'))['useModel']>
readonly useProjectCreationComposable: UnwrapRef<(typeof import('./src/composables/useProjectCreationComposable'))['useProjectCreationComposable']>
readonly useProjectListComposable: UnwrapRef<(typeof import('./src/composables/useProjectListComposable'))['useProjectListComposable']>
readonly useProjectListStore: UnwrapRef<(typeof import('./src/stores/project-list.store'))['useProjectListStore']>
readonly useRoute: UnwrapRef<(typeof import('vue-router'))['useRoute']>
readonly useRouter: UnwrapRef<(typeof import('vue-router'))['useRouter']>
readonly useSlots: UnwrapRef<(typeof import('vue'))['useSlots']>
readonly useTemplateRef: UnwrapRef<(typeof import('vue'))['useTemplateRef']>
readonly useToast: UnwrapRef<(typeof import('primevue/usetoast'))['useToast']>
readonly watch: UnwrapRef<(typeof import('vue'))['watch']>
readonly watchEffect: UnwrapRef<(typeof import('vue'))['watchEffect']>
readonly watchPostEffect: UnwrapRef<(typeof import('vue'))['watchPostEffect']>
Expand Down
5 changes: 5 additions & 0 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ declare module 'vue' {
Badge: typeof import('primevue/badge')['default']
Button: typeof import('primevue/button')['default']
Card: typeof import('primevue/card')['default']
Column: typeof import('primevue/column')['default']
ConfirmDialog: typeof import('primevue/confirmdialog')['default']
CreateProject: typeof import('./src/pages/CreateProject.vue')['default']
DataTable: typeof import('primevue/datatable')['default']
DefaultLayout: typeof import('./src/layouts/DefaultLayout.vue')['default']
FileUpload: typeof import('primevue/fileupload')['default']
Header: typeof import('./src/components/Header.vue')['default']
MainContent: typeof import('./src/components/MainContent.vue')['default']
Message: typeof import('primevue/message')['default']
OpenProject: typeof import('./src/pages/OpenProject.vue')['default']
ProgressSpinner: typeof import('primevue/progressspinner')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./src/components/Sidebar.vue')['default']
Toast: typeof import('primevue/toast')['default']
}
}
95 changes: 95 additions & 0 deletions frontend/src/composables/useProjectListComposable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
export const useProjectListComposable = () => {
const store = useProjectListStore()
const router = useRouter()
const confirm = useConfirm()
const toast = useToast()

const { projects, isLoading, error, hasProjects, projectCount } = storeToRefs(store)

// Load projects when composable is used
const loadProjects = async () => {
await store.fetchProjects()

if (error.value) {
toast.add({
severity: 'error',
summary: 'Error',
detail: error.value,
life: 5000,
})
}
}

// Open a project (navigate to project view)
const openProject = (projectId: string) => {
router.push(`/projects/${projectId}`)
}

// Delete a project with confirmation
const deleteProject = (project: { id: string; name: string }) => {
confirm.require({
message: `Are you sure you want to delete the project "${project.name}"? This action cannot be undone.`,
header: 'Delete Project',
icon: 'pi pi-exclamation-triangle',
rejectProps: {
label: 'Cancel',
severity: 'secondary',
outlined: true,
},
acceptProps: {
label: 'Delete',
severity: 'danger',
},
accept: async () => {
try {
await store.deleteProject(project.id)
toast.add({
severity: 'success',
summary: 'Success',
detail: `Project "${project.name}" has been deleted`,
life: 3000,
})
} catch (err) {
toast.add({
severity: 'error',
summary: 'Error',
detail: 'Failed to delete project',
life: 5000,
})
}
},
})
}

// Format date for display
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
}

// Clear any errors
const clearError = () => {
store.clearError()
}

return {
// State
projects,
isLoading,
error,
hasProjects,
projectCount,

// Actions
loadProjects,
openProject,
deleteProject,
formatDate,
clearError,
}
}
4 changes: 4 additions & 0 deletions frontend/src/layouts/DefaultLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@
</router-view>
</MainContent>
</div>

<!-- Global Toast and Confirm Dialog -->
<Toast />
<ConfirmDialog />
</div>
</template>
4 changes: 4 additions & 0 deletions frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { createPinia } from 'pinia'
import { createHead } from '@unhead/vue/client'
import PrimeVue from 'primevue/config'
import Aura from '@primeuix/themes/aura'
import ToastService from 'primevue/toastservice'
import ConfirmationService from 'primevue/confirmationservice'
import 'primeicons/primeicons.css'

// Import your router and app
Expand Down Expand Up @@ -39,6 +41,8 @@ app.use(router)
app.use(pinia)
app.use(head)
app.use(ApiPlugin)
app.use(ToastService)
app.use(ConfirmationService)

// Mount the app
app.mount('#app')
Loading