diff --git a/app/components/sites/SiteCard.vue b/app/components/sites/SiteCard.vue new file mode 100644 index 0000000..ce8f8bb --- /dev/null +++ b/app/components/sites/SiteCard.vue @@ -0,0 +1,63 @@ + + + diff --git a/app/components/sites/SiteCreateModal.vue b/app/components/sites/SiteCreateModal.vue new file mode 100644 index 0000000..d20ce2c --- /dev/null +++ b/app/components/sites/SiteCreateModal.vue @@ -0,0 +1,118 @@ + + + diff --git a/app/components/sites/SiteEditModal.vue b/app/components/sites/SiteEditModal.vue new file mode 100644 index 0000000..2414c6d --- /dev/null +++ b/app/components/sites/SiteEditModal.vue @@ -0,0 +1,221 @@ + + + diff --git a/app/composables/useSiteManagement.ts b/app/composables/useSiteManagement.ts new file mode 100644 index 0000000..5aff1c2 --- /dev/null +++ b/app/composables/useSiteManagement.ts @@ -0,0 +1,305 @@ +import { computed, onMounted, reactive, ref, watch } from 'vue' +import { navigateTo, useRoute } from '#imports' +import { useSites } from '~/composables/useSites' +import { useToast } from '~/composables/useToast' +import { useDocuments } from '~/composables/useDocuments' +import { getFileIcon } from '~/utils/fileIcons' +import { canPreviewDocument } from '~/utils/documentPreview' + +type SiteForm = { + name: string + contactName: string + contactPhone: string + contactAddress: string + contactPostalCode: string + contactCity: string +} + +type SiteDocument = { + id: string + name?: string + filename?: string + mimeType?: string + size?: number + path?: string +} + +type SiteWithDocuments = { + id: string + name?: string + contactName?: string + contactPhone?: string + contactAddress?: string + contactPostalCode?: string + contactCity?: string + documents?: SiteDocument[] + machines?: Array +} + +export function useSiteManagement() { + const route = useRoute() + const { showError, showSuccess } = useToast() + const { sites, loading, loadSites, createSite, updateSite, deleteSite } = useSites() + const { uploadDocuments, deleteDocument, loadDocumentsBySite } = useDocuments() + + const showAddSiteModal = ref(false) + const showEditSiteModal = ref(false) + + const siteBeingEdited = ref(null) + + const newSite = reactive({ + name: '', + contactName: '', + contactPhone: '', + contactAddress: '', + contactPostalCode: '', + contactCity: '' + }) + + const editSiteForm = reactive({ + name: '', + contactName: '', + contactPhone: '', + contactAddress: '', + contactPostalCode: '', + contactCity: '' + }) + + const selectedFiles = ref([]) + const uploadingDocuments = ref(false) + const previewDocument = ref(null) + const previewVisible = ref(false) + + const siteDocuments = computed(() => siteBeingEdited.value?.documents || []) + const documentIcon = (doc: SiteDocument) => + getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType }) + + const resetNewSite = () => { + newSite.name = '' + newSite.contactName = '' + newSite.contactPhone = '' + newSite.contactAddress = '' + newSite.contactPostalCode = '' + newSite.contactCity = '' + } + + const handleCreateSite = async () => { + const result = await createSite({ + name: newSite.name, + contactName: newSite.contactName, + contactPhone: newSite.contactPhone, + contactAddress: newSite.contactAddress, + contactPostalCode: newSite.contactPostalCode, + contactCity: newSite.contactCity + }) + + if (result.success) { + resetNewSite() + showAddSiteModal.value = false + } + } + + const editSite = (site: SiteWithDocuments) => { + siteBeingEdited.value = site + editSiteForm.name = site.name || '' + editSiteForm.contactName = site.contactName || '' + editSiteForm.contactPhone = site.contactPhone || '' + editSiteForm.contactAddress = site.contactAddress || '' + editSiteForm.contactPostalCode = site.contactPostalCode || '' + editSiteForm.contactCity = site.contactCity || '' + selectedFiles.value = [] + refreshSiteDocuments(site.id) + showEditSiteModal.value = true + } + + const closeEditModal = () => { + showEditSiteModal.value = false + siteBeingEdited.value = null + selectedFiles.value = [] + } + + const updateSiteInCollection = (id: string, updated: Partial) => { + const index = sites.value.findIndex(site => site.id === id) + if (index !== -1) { + sites.value[index] = { + ...sites.value[index], + ...updated + } + } + } + + const handleUpdateSite = async () => { + if (!siteBeingEdited.value) return + + const baseUpdate = { + name: editSiteForm.name, + contactName: editSiteForm.contactName, + contactPhone: editSiteForm.contactPhone, + contactAddress: editSiteForm.contactAddress, + contactPostalCode: editSiteForm.contactPostalCode, + contactCity: editSiteForm.contactCity + } + + const result = await updateSite(siteBeingEdited.value.id, baseUpdate) + + if (!result.success) return + + let uploadedDocuments: SiteDocument[] = [] + const existingDocuments = siteBeingEdited.value?.documents || [] + if (selectedFiles.value.length) { + uploadingDocuments.value = true + const uploadResult = await uploadDocuments( + { + files: selectedFiles.value, + context: { siteId: siteBeingEdited.value.id } + }, + { updateStore: false } + ) + uploadingDocuments.value = false + + if (uploadResult.success) { + uploadedDocuments = uploadResult.data || [] + selectedFiles.value = [] + } + } + + if (uploadedDocuments.length) { + const mergedDocuments = [...uploadedDocuments, ...existingDocuments] + if (siteBeingEdited.value) { + siteBeingEdited.value.documents = mergedDocuments + } + updateSiteInCollection(siteBeingEdited.value.id, { + ...baseUpdate, + documents: mergedDocuments + }) + } else { + updateSiteInCollection(siteBeingEdited.value.id, baseUpdate) + } + + closeEditModal() + } + + const handleRemoveSiteDocument = async (documentId: string) => { + if (!documentId) return + + const result = await deleteDocument(documentId, { updateStore: false }) + if (!result.success) return + + if (siteBeingEdited.value) { + siteBeingEdited.value.documents = (siteBeingEdited.value.documents || []).filter( + doc => doc.id !== documentId + ) + updateSiteInCollection(siteBeingEdited.value.id, { + documents: siteBeingEdited.value.documents + }) + } + } + + const downloadDocument = (doc: SiteDocument) => { + if (!doc?.path) return + + if (doc.path.startsWith('data:')) { + const link = document.createElement('a') + link.href = doc.path + link.download = doc.filename || doc.name || 'document' + link.click() + return + } + + window.open(doc.path, '_blank') + } + + const openPreview = (doc: SiteDocument) => { + if (!canPreviewDocument(doc)) return + previewDocument.value = doc + previewVisible.value = true + } + + const closePreview = () => { + previewVisible.value = false + previewDocument.value = null + } + + const refreshSiteDocuments = async (siteId: string) => { + if (!siteId) return + const result = await loadDocumentsBySite(siteId, { updateStore: false }) + if (result.success && siteBeingEdited.value && siteBeingEdited.value.id === siteId) { + const cloned = Array.isArray(result.data) ? [...result.data] : [] + siteBeingEdited.value.documents = cloned + updateSiteInCollection(siteId, { documents: cloned }) + } + } + + const formatSize = (size?: number | null) => { + if (size === undefined || size === null) return '—' + if (size === 0) return '0 B' + const units = ['B', 'KB', 'MB', 'GB'] + const index = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024))) + const formatted = size / Math.pow(1024, index) + return `${formatted.toFixed(1)} ${units[index]}` + } + + const confirmDeleteSite = async (site: SiteWithDocuments) => { + if ( + !confirm( + `Êtes-vous sûr de vouloir supprimer le site "${site.name}" ? Cette action est irréversible.` + ) + ) { + return + } + + try { + const result = await deleteSite(site.id) + if (result.success) { + showSuccess(`Site "${site.name}" supprimé avec succès`) + } else { + showError(`Erreur lors de la suppression: ${result.error}`) + } + } catch (error: any) { + showError(`Erreur lors de la suppression: ${error.message}`) + } + } + + onMounted(async () => { + await loadSites() + }) + + watch( + () => route.query.add, + async (shouldOpen) => { + if (shouldOpen === 'true') { + showAddSiteModal.value = true + await navigateTo('/sites', { replace: true }) + } + }, + { immediate: true } + ) + + return { + sites, + loading, + showAddSiteModal, + showEditSiteModal, + siteBeingEdited, + newSite, + editSiteForm, + selectedFiles, + uploadingDocuments, + previewDocument, + previewVisible, + siteDocuments, + documentIcon, + handleCreateSite, + editSite, + handleUpdateSite, + closeEditModal, + handleRemoveSiteDocument, + downloadDocument, + openPreview, + closePreview, + refreshSiteDocuments, + formatSize, + confirmDeleteSite, + canPreviewDocument + } +} diff --git a/app/pages/sites.vue b/app/pages/sites.vue index 5df4809..710803f 100644 --- a/app/pages/sites.vue +++ b/app/pages/sites.vue @@ -5,10 +5,8 @@ :visible="previewVisible" @close="closePreview" /> - - +
-

Sites

-
@@ -34,585 +31,76 @@
-
-
-
-

{{ site.name }}

-
{{ site.machines?.length || 0 }} machines
-
- -
-
-
- -
-
- -
-
- -
-
-
- -
- - -
-
-
+ :site="site" + @edit="editSite" + @delete="confirmDeleteSite" + />
- - - - - + +const { + sites, + loading, + showAddSiteModal, + showEditSiteModal, + siteBeingEdited, + newSite, + editSiteForm, + selectedFiles, + uploadingDocuments, + previewDocument, + previewVisible, + siteDocuments, + documentIcon, + handleCreateSite, + editSite, + handleUpdateSite, + closeEditModal, + handleRemoveSiteDocument, + downloadDocument, + openPreview, + closePreview, + formatSize, + confirmDeleteSite, + canPreviewDocument +} = useSiteManagement() +