update frontend ui, i18n, filters, and deps
This commit is contained in:
@@ -1,6 +1,216 @@
|
||||
<template>
|
||||
<main class="container">
|
||||
<h1>Emplacements</h1>
|
||||
<p>Arborescence a connecter a l'API.</p>
|
||||
<h1>{{ t('nav.emplacements') }}</h1>
|
||||
<p v-if="errorMessage">{{ errorMessage }}</p>
|
||||
|
||||
<section class="card" style="margin-bottom: 16px;">
|
||||
<h2>{{ t('filters.name') }}</h2>
|
||||
<div style="display: grid; gap: 8px;">
|
||||
<input v-model="filterNom" :placeholder="t('placeholders.name')" />
|
||||
<label>
|
||||
{{ t('filters.limit') }}
|
||||
<select v-model.number="limit">
|
||||
<option :value="10">10</option>
|
||||
<option :value="25">25</option>
|
||||
<option :value="50">50</option>
|
||||
</select>
|
||||
</label>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="card" type="button" @click="resetFilters">
|
||||
{{ t('filters.reset') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<EmplacementForm
|
||||
v-model="form"
|
||||
:saving="saving"
|
||||
:message="message"
|
||||
:mode="editingId ? 'edit' : 'create'"
|
||||
@save="saveEmplacement"
|
||||
@cancel="resetForm"
|
||||
/>
|
||||
|
||||
<p v-if="pending">{{ t('states.loading') }}</p>
|
||||
<p v-else-if="items.length === 0">{{ t('states.empty') }}</p>
|
||||
<section v-else class="grid">
|
||||
<article v-for="item in items" :key="item.id" class="card">
|
||||
<h3>{{ item.nom }}</h3>
|
||||
<p v-if="item.piece">{{ t('labels.piece') }}: {{ item.piece }}</p>
|
||||
<p v-if="item.meuble">{{ t('labels.meuble') }}: {{ item.meuble }}</p>
|
||||
<small v-if="item.numero_boite">{{ t('labels.numeroBoite') }}: {{ item.numero_boite }}</small>
|
||||
<div style="margin-top: 8px; display: flex; gap: 8px;">
|
||||
<button class="card" type="button" @click="editEmplacement(item)">{{ t('actions.edit') }}</button>
|
||||
<button class="card" type="button" @click="confirmDelete(item.id)">{{ t('actions.delete') }}</button>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section v-if="items.length" class="card" style="margin-top: 16px;">
|
||||
<h2>{{ t('tree.title') }}</h2>
|
||||
<TreeList :items="items" />
|
||||
</section>
|
||||
|
||||
<section class="card" style="margin-top: 16px;">
|
||||
<p>{{ t('pagination.page') }} {{ page }} / {{ totalPages }}</p>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="card" type="button" :disabled="page <= 1" @click="page--">
|
||||
{{ t('pagination.prev') }}
|
||||
</button>
|
||||
<button class="card" type="button" :disabled="page >= totalPages" @click="page++">
|
||||
{{ t('pagination.next') }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ConfirmDialog
|
||||
:open="confirmOpen"
|
||||
:title="t('confirm.deleteEmplacementTitle')"
|
||||
:message="t('confirm.deleteEmplacementMessage')"
|
||||
@confirm="runConfirm"
|
||||
@cancel="closeConfirm"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Emplacement = {
|
||||
id: string
|
||||
nom: string
|
||||
parent_id?: string | null
|
||||
piece?: string | null
|
||||
meuble?: string | null
|
||||
numero_boite?: string | null
|
||||
}
|
||||
|
||||
const { apiBase, getErrorMessage } = useApi()
|
||||
const { t } = useI18n()
|
||||
|
||||
const page = ref(1)
|
||||
const limit = ref(50)
|
||||
const filterNom = ref('')
|
||||
|
||||
const { data, pending, error, refresh } = await useFetch<{
|
||||
items: Emplacement[]
|
||||
meta?: { total: number; page: number; limit: number }
|
||||
}>(`${apiBase}/emplacements`, {
|
||||
query: {
|
||||
page,
|
||||
limit,
|
||||
nom: filterNom
|
||||
},
|
||||
watch: [page, limit, filterNom]
|
||||
})
|
||||
|
||||
const items = computed(() => data.value?.items ?? [])
|
||||
const total = computed(() => data.value?.meta?.total ?? items.value.length)
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / limit.value)))
|
||||
const errorMessage = computed(() =>
|
||||
error.value ? getErrorMessage(error.value, t('messages.loadError')) : ""
|
||||
)
|
||||
|
||||
const saving = ref(false)
|
||||
const message = ref("")
|
||||
const editingId = ref<string | null>(null)
|
||||
const confirmOpen = ref(false)
|
||||
const confirmAction = ref<null | (() => Promise<void>)>(null)
|
||||
const form = ref({
|
||||
nom: "",
|
||||
parent_id: "",
|
||||
piece: "",
|
||||
meuble: "",
|
||||
numero_boite: ""
|
||||
})
|
||||
|
||||
watch([filterNom, limit], () => {
|
||||
page.value = 1
|
||||
})
|
||||
|
||||
const resetFilters = () => {
|
||||
filterNom.value = ''
|
||||
limit.value = 50
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
editingId.value = null
|
||||
form.value = { nom: "", parent_id: "", piece: "", meuble: "", numero_boite: "" }
|
||||
}
|
||||
|
||||
const editEmplacement = (item: Emplacement) => {
|
||||
editingId.value = item.id
|
||||
form.value = {
|
||||
nom: item.nom,
|
||||
parent_id: item.parent_id || "",
|
||||
piece: item.piece || "",
|
||||
meuble: item.meuble || "",
|
||||
numero_boite: item.numero_boite || ""
|
||||
}
|
||||
}
|
||||
|
||||
const saveEmplacement = async () => {
|
||||
message.value = ""
|
||||
if (!form.value.nom) {
|
||||
message.value = t('messages.requiredName')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = {
|
||||
nom: form.value.nom,
|
||||
parent_id: form.value.parent_id || undefined,
|
||||
piece: form.value.piece || undefined,
|
||||
meuble: form.value.meuble || undefined,
|
||||
numero_boite: form.value.numero_boite || undefined
|
||||
}
|
||||
if (editingId.value) {
|
||||
await $fetch(`${apiBase}/emplacements/${editingId.value}`, {
|
||||
method: "PUT",
|
||||
body: payload
|
||||
})
|
||||
message.value = t('messages.updated')
|
||||
} else {
|
||||
await $fetch(`${apiBase}/emplacements`, {
|
||||
method: "POST",
|
||||
body: payload
|
||||
})
|
||||
message.value = t('messages.created')
|
||||
}
|
||||
resetForm()
|
||||
await refresh()
|
||||
} catch (err) {
|
||||
message.value = getErrorMessage(err, t('messages.saveError'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const deleteEmplacement = async (id: string) => {
|
||||
message.value = ""
|
||||
try {
|
||||
await $fetch(`${apiBase}/emplacements/${id}`, { method: "DELETE" })
|
||||
message.value = t('messages.deleted')
|
||||
await refresh()
|
||||
} catch (err) {
|
||||
message.value = getErrorMessage(err, t('messages.deleteError'))
|
||||
}
|
||||
}
|
||||
|
||||
const confirmDelete = (id: string) => {
|
||||
confirmAction.value = () => deleteEmplacement(id)
|
||||
confirmOpen.value = true
|
||||
}
|
||||
|
||||
const closeConfirm = () => {
|
||||
confirmOpen.value = false
|
||||
confirmAction.value = null
|
||||
}
|
||||
|
||||
const runConfirm = async () => {
|
||||
if (confirmAction.value) {
|
||||
await confirmAction.value()
|
||||
}
|
||||
closeConfirm()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user