feat(documents) : add type column, filter, and edit to documents page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ interface LoadDocumentsOptions {
|
|||||||
orderBy?: string
|
orderBy?: string
|
||||||
orderDir?: 'asc' | 'desc'
|
orderDir?: 'asc' | 'desc'
|
||||||
attachmentFilter?: string
|
attachmentFilter?: string
|
||||||
|
type?: string
|
||||||
force?: boolean
|
force?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,10 +106,11 @@ export function useDocuments() {
|
|||||||
orderBy = 'createdAt',
|
orderBy = 'createdAt',
|
||||||
orderDir = 'desc',
|
orderDir = 'desc',
|
||||||
attachmentFilter = 'all',
|
attachmentFilter = 'all',
|
||||||
|
type = 'all',
|
||||||
force = false,
|
force = false,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
if (!force && loaded.value && !search && page === 1 && attachmentFilter === 'all') {
|
if (!force && loaded.value && !search && page === 1 && attachmentFilter === 'all' && type === 'all') {
|
||||||
return { success: true, data: documents.value }
|
return { success: true, data: documents.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +132,10 @@ export function useDocuments() {
|
|||||||
params.set(`exists[${attachmentFilter}]`, 'true')
|
params.set(`exists[${attachmentFilter}]`, 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type && type !== 'all') {
|
||||||
|
params.set('type', type)
|
||||||
|
}
|
||||||
|
|
||||||
params.set(`order[${orderBy}]`, orderDir)
|
params.set(`order[${orderBy}]`, orderDir)
|
||||||
|
|
||||||
const result = await get(`/documents?${params.toString()}`)
|
const result = await get(`/documents?${params.toString()}`)
|
||||||
|
|||||||
@@ -7,6 +7,13 @@
|
|||||||
@close="closePreview"
|
@close="closePreview"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DocumentEditModal
|
||||||
|
:visible="editModalVisible"
|
||||||
|
:document="editingDocument"
|
||||||
|
@close="editModalVisible = false"
|
||||||
|
@updated="handleDocumentUpdated"
|
||||||
|
/>
|
||||||
|
|
||||||
<section class="card bg-base-100 shadow-sm">
|
<section class="card bg-base-100 shadow-sm">
|
||||||
<div class="card-body space-y-6">
|
<div class="card-body space-y-6">
|
||||||
<DataTable
|
<DataTable
|
||||||
@@ -55,6 +62,26 @@
|
|||||||
<option value="product">Produits</option>
|
<option value="product">Produits</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label
|
||||||
|
class="text-xs font-semibold uppercase tracking-wide text-base-content/70"
|
||||||
|
for="doc-type-filter"
|
||||||
|
>
|
||||||
|
Type
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="doc-type-filter"
|
||||||
|
v-model="typeFilter"
|
||||||
|
class="select select-bordered select-sm"
|
||||||
|
@change="table.handleFilterChange"
|
||||||
|
>
|
||||||
|
<option value="all">Tous</option>
|
||||||
|
<option v-for="t in DOCUMENT_TYPES" :key="t.value" :value="t.value">
|
||||||
|
{{ t.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell-name="{ row }">
|
<template #cell-name="{ row }">
|
||||||
@@ -77,6 +104,10 @@
|
|||||||
{{ row.mimeType || 'Inconnu' }}
|
{{ row.mimeType || 'Inconnu' }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #cell-type="{ row }">
|
||||||
|
<span class="badge badge-sm badge-outline">{{ getDocumentTypeLabel(row.type || 'documentation') }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #cell-size="{ row }">
|
<template #cell-size="{ row }">
|
||||||
{{ formatSize(row.size) }}
|
{{ formatSize(row.size) }}
|
||||||
</template>
|
</template>
|
||||||
@@ -98,6 +129,14 @@
|
|||||||
|
|
||||||
<template #cell-actions="{ row }">
|
<template #cell-actions="{ row }">
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
v-if="canEdit"
|
||||||
|
class="btn btn-ghost btn-xs"
|
||||||
|
type="button"
|
||||||
|
@click="openEditModal(row)"
|
||||||
|
>
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-ghost btn-xs"
|
class="btn btn-ghost btn-xs"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -123,12 +162,15 @@ import { computed, onMounted, ref, type Ref } from 'vue'
|
|||||||
import DataTable from '~/components/common/DataTable.vue'
|
import DataTable from '~/components/common/DataTable.vue'
|
||||||
import { useDocuments } from '~/composables/useDocuments'
|
import { useDocuments } from '~/composables/useDocuments'
|
||||||
import { useDataTable } from '~/composables/useDataTable'
|
import { useDataTable } from '~/composables/useDataTable'
|
||||||
|
import { usePermissions } from '~/composables/usePermissions'
|
||||||
import { getFileIcon } from '~/utils/fileIcons'
|
import { getFileIcon } from '~/utils/fileIcons'
|
||||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||||
import { formatFrenchDate } from '~/utils/date'
|
import { formatFrenchDate } from '~/utils/date'
|
||||||
|
import { DOCUMENT_TYPES, getDocumentTypeLabel } from '~/shared/documentTypes'
|
||||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||||
|
|
||||||
const { documents, total, loading, loadDocuments } = useDocuments()
|
const { documents, total, loading, loadDocuments, updateDocument } = useDocuments()
|
||||||
|
const { canEdit } = usePermissions()
|
||||||
|
|
||||||
const table = useDataTable(
|
const table = useDataTable(
|
||||||
{ fetchData: fetchDocuments },
|
{ fetchData: fetchDocuments },
|
||||||
@@ -139,21 +181,26 @@ const table = useDataTable(
|
|||||||
persistToUrl: true,
|
persistToUrl: true,
|
||||||
extraParams: {
|
extraParams: {
|
||||||
filter: { default: 'all' },
|
filter: { default: 'all' },
|
||||||
|
typeFilter: { default: 'all' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const attachmentFilter = table.filters.filter as Ref<string>
|
const attachmentFilter = table.filters.filter as Ref<string>
|
||||||
|
const typeFilter = table.filters.typeFilter as Ref<string>
|
||||||
|
|
||||||
const previewDocument = ref<any>(null)
|
const previewDocument = ref<any>(null)
|
||||||
const previewVisible = ref(false)
|
const previewVisible = ref(false)
|
||||||
|
const editingDocument = ref<any>(null)
|
||||||
|
const editModalVisible = ref(false)
|
||||||
|
|
||||||
const documentsOnPage = computed(() => documents.value.length)
|
const documentsOnPage = computed(() => documents.value.length)
|
||||||
const paginationState = table.pagination(total, documentsOnPage)
|
const paginationState = table.pagination(total, documentsOnPage)
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'name', label: 'Nom', sortable: true, sortKey: 'name' },
|
{ key: 'name', label: 'Nom', sortable: true, sortKey: 'name' },
|
||||||
{ key: 'mimeType', label: 'Type' },
|
{ key: 'mimeType', label: 'Type MIME' },
|
||||||
|
{ key: 'type', label: 'Type' },
|
||||||
{ key: 'size', label: 'Taille', sortable: true, sortKey: 'size' },
|
{ key: 'size', label: 'Taille', sortable: true, sortKey: 'size' },
|
||||||
{ key: 'attachment', label: 'Rattaché à' },
|
{ key: 'attachment', label: 'Rattaché à' },
|
||||||
{ key: 'createdAt', label: 'Date', sortable: true, sortKey: 'createdAt' },
|
{ key: 'createdAt', label: 'Date', sortable: true, sortKey: 'createdAt' },
|
||||||
@@ -168,6 +215,7 @@ async function fetchDocuments() {
|
|||||||
orderBy: table.sortField.value,
|
orderBy: table.sortField.value,
|
||||||
orderDir: table.sortDirection.value as 'asc' | 'desc',
|
orderDir: table.sortDirection.value as 'asc' | 'desc',
|
||||||
attachmentFilter: attachmentFilter.value,
|
attachmentFilter: attachmentFilter.value,
|
||||||
|
type: typeFilter.value,
|
||||||
force: true,
|
force: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -198,6 +246,25 @@ const closePreview = () => {
|
|||||||
previewDocument.value = null
|
previewDocument.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openEditModal = (doc: any) => {
|
||||||
|
editingDocument.value = doc
|
||||||
|
editModalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDocumentUpdated = async (data: { name: string; type: string }) => {
|
||||||
|
if (!editingDocument.value?.id) return
|
||||||
|
const result = await updateDocument(editingDocument.value.id, data)
|
||||||
|
if (result.success) {
|
||||||
|
const doc = documents.value.find((d) => d.id === editingDocument.value.id)
|
||||||
|
if (doc) {
|
||||||
|
doc.name = data.name
|
||||||
|
doc.type = data.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editModalVisible.value = false
|
||||||
|
editingDocument.value = null
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchDocuments()
|
fetchDocuments()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user