feat(catalog) : M6 — écran consultation produit + onglets conditionnés + édition sans redirection
Auto Tag Develop / tag (push) Successful in 37s
Auto Tag Develop / tag (push) Successful in 37s
- Nouvel écran de consultation lecture seule /admin/products/{id} (calque
client/fournisseur) : clic sur une ligne ouvre la consultation (plus l'édition
directe), bouton « Modifier » → édition.
- Règle ERP-193 en consultation : champs vides / checkbox non cochées masqués
(isFilled) ; onglets vides masqués → les coquilles Fournisseurs/Clients
(placeholder, module Contrat inexistant) ne sont pas rendues en consultation.
- Onglets Fournisseurs/Clients : non affichés à l'ajout (avant validation du
formulaire principal) ; visibilité conditionnée par l'état (spec C3, « Aucun »
= OTHER) : Fournisseurs si Achat/Aucun, Clients si Vendu/Aucun.
- Édition : après « Enregistrer » on reste sur l'écran (l'utilisateur garde la
main, calque client/fournisseur) ; réaffichage des valeurs normalisées serveur
(RG-6.07) via re-prefill, plus de redirection.
- i18n consultation + tests (consultation, onglets, no-redirect) ; spec écran 8.bis.
This commit is contained in:
@@ -102,8 +102,10 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Onglets Fournisseurs / Clients en placeholder (HP-M6-01). -->
|
||||
<ProductPlaceholderTabs />
|
||||
<!-- Onglets Fournisseurs / Clients en placeholder (HP-M6-01). Visibilite
|
||||
conditionnee par l'etat : Fournisseurs si Achat/Aucun, Clients si
|
||||
Vendu/Aucun (cf. ProductPlaceholderTabs). -->
|
||||
<ProductPlaceholderTabs :states="form.states" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -119,13 +121,14 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { can } = usePermissions()
|
||||
|
||||
// Gating de la route : la modification est reservee a `manage` (catalogue admin-only).
|
||||
if (!can('catalog.products.manage')) {
|
||||
await navigateTo('/admin/products')
|
||||
}
|
||||
|
||||
const productId = route.params.id as string
|
||||
|
||||
// Gating de la route : la modification est reservee a `manage` ; sinon retour
|
||||
// consultation (la lecture seule reste accessible avec `view`).
|
||||
if (!can('catalog.products.manage')) {
|
||||
await navigateTo(`/admin/products/${productId}`)
|
||||
}
|
||||
|
||||
const { product, loading, error, load } = useProduct(productId)
|
||||
|
||||
const {
|
||||
@@ -154,17 +157,19 @@ const stateOptions = computed(() =>
|
||||
PRODUCT_STATES.map(code => ({ value: code, label: t(`admin.products.state.${code}`) })),
|
||||
)
|
||||
|
||||
/** Retour vers le catalogue produit (fleche d'en-tete). */
|
||||
/** Retour vers la consultation du produit (fleche d'en-tete). */
|
||||
function goBack(): void {
|
||||
router.push('/admin/products')
|
||||
router.push(`/admin/products/${productId}`)
|
||||
}
|
||||
|
||||
/** Soumet la modification (PATCH) ; au succes, retour a la liste. */
|
||||
/**
|
||||
* Soumet la modification (PATCH). Au succes : on RESTE sur l'ecran d'edition
|
||||
* (l'utilisateur garde la main, calque client/fournisseur) — le toast de succes et
|
||||
* la reaffichage des valeurs normalisees sont geres par `submit()`. La navigation
|
||||
* reste manuelle (fleche retour -> consultation).
|
||||
*/
|
||||
async function onSubmit(): Promise<void> {
|
||||
const ok = await submit()
|
||||
if (ok) {
|
||||
router.push('/admin/products')
|
||||
}
|
||||
await submit()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- En-tete : retour catalogue + nom du produit + action « Modifier ». -->
|
||||
<div class="flex items-center gap-3 pt-11">
|
||||
<MalioButtonIcon
|
||||
icon="mdi:arrow-left-bold"
|
||||
icon-size="24"
|
||||
variant="ghost"
|
||||
:title="t('admin.products.consultation.back')"
|
||||
v-bind="{ ariaLabel: t('admin.products.consultation.back') }"
|
||||
@click="goBack"
|
||||
/>
|
||||
<h1 class="text-[30px] font-semibold text-m-primary">{{ headerTitle }}</h1>
|
||||
|
||||
<div class="ml-auto flex items-center gap-12">
|
||||
<MalioButton
|
||||
v-if="canManage"
|
||||
variant="secondary"
|
||||
icon-name="mdi:pencil-outline"
|
||||
icon-position="left"
|
||||
:label="t('admin.products.action.edit')"
|
||||
@click="goEdit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Etats de chargement / introuvable. -->
|
||||
<p v-if="loading" class="mt-12 text-center text-black/60">{{ t('admin.products.consultation.loading') }}</p>
|
||||
<p v-else-if="error" class="mt-12 text-center text-m-danger">{{ t('admin.products.consultation.notFound') }}</p>
|
||||
|
||||
<template v-else-if="product">
|
||||
<!-- ── Bloc principal (lecture seule) — meme disposition que l'ajout/edition.
|
||||
Champs non remplis masques (ERP-193, isFilled). -->
|
||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||
<MalioInputText
|
||||
v-if="isFilled(statesLabel)"
|
||||
:model-value="statesLabel"
|
||||
:label="t('admin.products.form.states')"
|
||||
disabled
|
||||
/>
|
||||
<MalioInputText
|
||||
v-if="isFilled(sitesLabel)"
|
||||
:model-value="sitesLabel"
|
||||
:label="t('admin.products.form.sites')"
|
||||
disabled
|
||||
/>
|
||||
<MalioInputText
|
||||
v-if="isFilled(product.name)"
|
||||
:model-value="product.name"
|
||||
:label="t('admin.products.form.name')"
|
||||
disabled
|
||||
/>
|
||||
<MalioInputText
|
||||
v-if="isFilled(product.code)"
|
||||
:model-value="product.code"
|
||||
:label="t('admin.products.form.code')"
|
||||
disabled
|
||||
/>
|
||||
<MalioInputText
|
||||
v-if="isFilled(categoryLabel)"
|
||||
:model-value="categoryLabel"
|
||||
:label="t('admin.products.form.category')"
|
||||
disabled
|
||||
/>
|
||||
<MalioInputText
|
||||
v-if="isFilled(storageTypesLabel)"
|
||||
:model-value="storageTypesLabel"
|
||||
:label="t('admin.products.form.storageTypes')"
|
||||
disabled
|
||||
/>
|
||||
<!-- RG-6.03 : « Fabriqué » / « Contient de la mélasse » affiches
|
||||
uniquement si l'etat contient « Vendu » ET la case est cochee. -->
|
||||
<div v-if="isSale && isFilled(product.manufactured)" class="flex h-12 items-center">
|
||||
<MalioCheckbox
|
||||
id="product-view-manufactured"
|
||||
:label="t('admin.products.form.manufactured')"
|
||||
:model-value="product.manufactured"
|
||||
disabled
|
||||
:reserve-message-space="false"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="isSale && isFilled(product.containsMolasses)" class="flex h-12 items-center">
|
||||
<MalioCheckbox
|
||||
id="product-view-molasses"
|
||||
:label="t('admin.products.form.containsMolasses')"
|
||||
:model-value="product.containsMolasses"
|
||||
disabled
|
||||
:reserve-message-space="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pas d'onglet en consultation (ERP-193) : on masque les onglets vides.
|
||||
Les onglets Fournisseurs / Clients sont des coquilles non implementees
|
||||
(placeholder, module Contrat inexistant, HP-M6-01) => aucune donnee a
|
||||
afficher, donc rien n'est rendu ici. Ils restent visibles a l'edition
|
||||
(preview + regle d'etat). Quand le module Contrat existera, ce bloc
|
||||
affichera les onglets effectivement remplis (calque client/fournisseur). -->
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useProduct } from '~/modules/catalog/composables/useProduct'
|
||||
import { isFilled } from '~/shared/utils/consultationDisplay'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { can } = usePermissions()
|
||||
|
||||
// Gating de la route : la consultation est reservee a `view` (catalogue admin-only).
|
||||
if (!can('catalog.products.view')) {
|
||||
await navigateTo('/admin/products')
|
||||
}
|
||||
|
||||
const productId = route.params.id as string
|
||||
|
||||
const { product, loading, error, load } = useProduct(productId)
|
||||
|
||||
// L'edition est reservee a `manage` ; le bouton « Modifier » suit cette permission.
|
||||
const canManage = computed(() => can('catalog.products.manage'))
|
||||
|
||||
const headerTitle = computed(() => product.value?.name ?? t('admin.products.consultation.title'))
|
||||
|
||||
useHead({ title: t('admin.products.consultation.title') })
|
||||
|
||||
// RG-6.03 : « Vendu » conditionne l'affichage des booleens fabriqué / mélasse.
|
||||
const isSale = computed(() => product.value?.states.includes('SALE') ?? false)
|
||||
|
||||
// ── Libelles lecture seule (relations embarquees mappees en texte) ───────────
|
||||
const statesLabel = computed(() =>
|
||||
(product.value?.states ?? []).map(code => t(`admin.products.state.${code}`)).join(', '),
|
||||
)
|
||||
const sitesLabel = computed(() =>
|
||||
(product.value?.sites ?? []).map(site => site.name).join(', '),
|
||||
)
|
||||
const categoryLabel = computed(() => product.value?.category?.name ?? '')
|
||||
const storageTypesLabel = computed(() =>
|
||||
(product.value?.storageTypes ?? []).map(type => type.label).join(', '),
|
||||
)
|
||||
|
||||
/** Retour vers le catalogue produit (fleche d'en-tete). */
|
||||
function goBack(): void {
|
||||
router.push('/admin/products')
|
||||
}
|
||||
|
||||
/** Bascule vers l'ecran de modification. */
|
||||
function goEdit(): void {
|
||||
router.push(`/admin/products/${productId}/edit`)
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -186,9 +186,9 @@ const columns = [
|
||||
{ key: 'categoryName', label: t('admin.products.column.category') },
|
||||
]
|
||||
|
||||
/** Clic sur une ligne → ecran d'edition (route imbriquee /admin/products/{id}/edit). */
|
||||
/** Clic sur une ligne → ecran de consultation (lecture seule) /admin/products/{id}. */
|
||||
function onRowClick(item: Record<string, unknown>): void {
|
||||
router.push(`/admin/products/${item.id}/edit`)
|
||||
router.push(`/admin/products/${item.id}`)
|
||||
}
|
||||
|
||||
function goToCreate(): void {
|
||||
|
||||
@@ -97,9 +97,9 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Onglets Fournisseurs / Clients en placeholder (HP-M6-01) : presents
|
||||
des l'ajout pour coherence avec l'ecran de modification. -->
|
||||
<ProductPlaceholderTabs />
|
||||
<!-- Onglets Fournisseurs / Clients (placeholder, HP-M6-01) : NON affiches a
|
||||
l'ajout. Ils n'apparaissent qu'apres validation du formulaire principal
|
||||
(ecran de modification), une fois le produit cree. -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user