feat(model-types): allow adding custom fields in restricted mode
When a category has linked items (pieces, components, products), enable restricted mode instead of blocking all edits: - Allow adding new custom fields - Lock existing fields from modification or deletion - Hide add buttons for products, pieces, and subcomponents - Display informative message about restricted mode Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
:locked-type-label="displayedRootTypeLabel"
|
||||
:allow-subcomponents="allowSubcomponents"
|
||||
:max-subcomponent-depth="maxSubcomponentDepth"
|
||||
:restricted-mode="restrictedMode"
|
||||
is-root
|
||||
/>
|
||||
</div>
|
||||
@@ -55,6 +56,10 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: Infinity,
|
||||
},
|
||||
restrictedMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
Produits inclus par défaut
|
||||
</h3>
|
||||
<p class="text-xs text-base-content/70">
|
||||
Ces produits s’afficheront lors de la création d’une pièce basée sur cette catégorie.
|
||||
Ces produits s'afficheront lors de la création d'une pièce basée sur cette catégorie.
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline btn-xs" @click="addProduct">
|
||||
<button v-if="!restrictedMode" type="button" class="btn btn-outline btn-xs" @click="addProduct">
|
||||
<IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
|
||||
Ajouter
|
||||
</button>
|
||||
@@ -35,6 +35,7 @@
|
||||
<select
|
||||
v-model="product.typeProductId"
|
||||
class="select select-bordered select-xs"
|
||||
:disabled="isProductLocked(product)"
|
||||
@change="handleProductTypeSelect(product)"
|
||||
>
|
||||
<option value="">
|
||||
@@ -51,12 +52,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="!isProductLocked(product)"
|
||||
type="button"
|
||||
class="btn btn-error btn-xs btn-square"
|
||||
@click="removeProduct(index)"
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else class="tooltip tooltip-left" data-tip="Ce produit ne peut pas être supprimé car des éléments utilisent cette catégorie">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed"
|
||||
disabled
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -107,8 +118,9 @@
|
||||
type="text"
|
||||
class="input input-bordered input-xs"
|
||||
placeholder="Nom du champ"
|
||||
:disabled="isFieldLocked(field)"
|
||||
>
|
||||
<select v-model="field.type" class="select select-bordered select-xs">
|
||||
<select v-model="field.type" class="select select-bordered select-xs" :disabled="isFieldLocked(field)">
|
||||
<option value="text">
|
||||
Texte
|
||||
</option>
|
||||
@@ -128,7 +140,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<input v-model="field.required" type="checkbox" class="checkbox checkbox-xs">
|
||||
<input v-model="field.required" type="checkbox" class="checkbox checkbox-xs" :disabled="isFieldLocked(field)">
|
||||
Obligatoire
|
||||
</div>
|
||||
|
||||
@@ -137,16 +149,27 @@
|
||||
v-model="field.optionsText"
|
||||
class="textarea textarea-bordered textarea-xs h-20"
|
||||
placeholder="Option 1 Option 2"
|
||||
:disabled="isFieldLocked(field)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="!isFieldLocked(field)"
|
||||
type="button"
|
||||
class="btn btn-error btn-xs btn-square"
|
||||
@click="removeField(index)"
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else class="tooltip tooltip-left" data-tip="Ce champ ne peut pas être supprimé car des éléments utilisent cette catégorie">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed"
|
||||
disabled
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -181,6 +204,7 @@ type EditorProduct = {
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: PieceModelStructure | null
|
||||
restrictedMode?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -330,6 +354,19 @@ const fields = ref<EditorField[]>(hydrateFields(props.modelValue))
|
||||
const products = ref<EditorProduct[]>(hydrateProducts(props.modelValue))
|
||||
const restState = ref<Record<string, unknown>>(extractRest(props.modelValue))
|
||||
|
||||
const initialFieldUids = ref<Set<string>>(new Set(fields.value.map(f => f.uid)))
|
||||
const initialProductUids = ref<Set<string>>(new Set(products.value.map(p => p.uid)))
|
||||
|
||||
const isFieldLocked = (field: EditorField): boolean => {
|
||||
return props.restrictedMode === true && initialFieldUids.value.has(field.uid)
|
||||
}
|
||||
|
||||
const isProductLocked = (product: EditorProduct): boolean => {
|
||||
return props.restrictedMode === true && initialProductUids.value.has(product.uid)
|
||||
}
|
||||
|
||||
const restrictedMode = computed(() => props.restrictedMode === true)
|
||||
|
||||
const applyOrderIndex = (list: EditorField[]): EditorField[] =>
|
||||
list.map((field, index) => ({
|
||||
...field,
|
||||
@@ -438,6 +475,8 @@ watch(
|
||||
products.value = hydrateProducts(value)
|
||||
products.value.forEach((product) => updateProductTypeMetadata(product))
|
||||
lastEmitted = incomingSerialized
|
||||
initialFieldUids.value = new Set(fields.value.map(f => f.uid))
|
||||
initialProductUids.value = new Set(products.value.map(p => p.uid))
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<select
|
||||
v-model="node.typeComposantId"
|
||||
class="select select-bordered select-sm w-full"
|
||||
:disabled="isLocked"
|
||||
@change="handleComponentTypeSelect(node)"
|
||||
>
|
||||
<option value="">
|
||||
@@ -42,6 +43,7 @@
|
||||
type="text"
|
||||
class="input input-bordered input-xs"
|
||||
placeholder="Alias du sous-composant"
|
||||
:disabled="isLocked"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -52,13 +54,18 @@
|
||||
</template>
|
||||
</div>
|
||||
<button
|
||||
v-if="!isRoot"
|
||||
v-if="!isRoot && !isLocked"
|
||||
type="button"
|
||||
class="btn btn-error btn-xs btn-square"
|
||||
@click="emit('remove')"
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else-if="!isRoot && isLocked" class="tooltip tooltip-left" data-tip="Ce sous-composant ne peut pas être supprimé">
|
||||
<button type="button" class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed" disabled>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-4 space-y-5">
|
||||
@@ -107,8 +114,9 @@
|
||||
type="text"
|
||||
class="input input-bordered input-xs"
|
||||
placeholder="Nom du champ"
|
||||
:disabled="isCustomFieldLocked(index)"
|
||||
/>
|
||||
<select v-model="field.type" class="select select-bordered select-xs">
|
||||
<select v-model="field.type" class="select select-bordered select-xs" :disabled="isCustomFieldLocked(index)">
|
||||
<option value="text">Texte</option>
|
||||
<option value="number">Nombre</option>
|
||||
<option value="select">Liste</option>
|
||||
@@ -117,7 +125,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<input v-model="field.required" type="checkbox" class="checkbox checkbox-xs" />
|
||||
<input v-model="field.required" type="checkbox" class="checkbox checkbox-xs" :disabled="isCustomFieldLocked(index)" />
|
||||
Obligatoire
|
||||
</div>
|
||||
<textarea
|
||||
@@ -125,15 +133,26 @@
|
||||
v-model="field.optionsText"
|
||||
class="textarea textarea-bordered textarea-xs h-20"
|
||||
placeholder="Option 1 Option 2"
|
||||
:disabled="isCustomFieldLocked(index)"
|
||||
></textarea>
|
||||
</div>
|
||||
<button
|
||||
v-if="!isCustomFieldLocked(index)"
|
||||
type="button"
|
||||
class="btn btn-error btn-xs btn-square"
|
||||
@click="removeCustomField(index)"
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else class="tooltip tooltip-left" data-tip="Ce champ ne peut pas être supprimé car des éléments utilisent cette catégorie">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed"
|
||||
disabled
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -144,7 +163,7 @@
|
||||
<h4 :class="headingClass">
|
||||
{{ isRoot ? 'Produits inclus par défaut' : 'Produits' }}
|
||||
</h4>
|
||||
<button type="button" class="btn btn-outline btn-xs" @click="addProduct">
|
||||
<button v-if="!restrictedMode" type="button" class="btn btn-outline btn-xs" @click="addProduct">
|
||||
<IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
|
||||
Ajouter
|
||||
</button>
|
||||
@@ -179,6 +198,7 @@
|
||||
<select
|
||||
v-model="product.typeProductId"
|
||||
class="select select-bordered select-xs"
|
||||
:disabled="isProductLocked(index)"
|
||||
@change="handleProductTypeSelect(product)"
|
||||
>
|
||||
<option value="">
|
||||
@@ -194,9 +214,18 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-error btn-xs btn-square" @click="removeProduct(index)">
|
||||
<button v-if="!isProductLocked(index)" type="button" class="btn btn-error btn-xs btn-square" @click="removeProduct(index)">
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else class="tooltip tooltip-left" data-tip="Ce produit ne peut pas être supprimé car des éléments utilisent cette catégorie">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed"
|
||||
disabled
|
||||
>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -207,7 +236,7 @@
|
||||
<h4 :class="headingClass">
|
||||
{{ isRoot ? 'Pièces incluses par défaut' : 'Pièces' }}
|
||||
</h4>
|
||||
<button type="button" class="btn btn-outline btn-xs" @click="addPiece">
|
||||
<button v-if="!restrictedMode" type="button" class="btn btn-outline btn-xs" @click="addPiece">
|
||||
<IconLucidePlus class="w-3 h-3 mr-2" aria-hidden="true" />
|
||||
Ajouter
|
||||
</button>
|
||||
@@ -243,6 +272,7 @@
|
||||
<select
|
||||
v-model="piece.typePieceId"
|
||||
class="select select-bordered select-xs"
|
||||
:disabled="isPieceLocked(index)"
|
||||
@change="handlePieceTypeSelect(piece)"
|
||||
>
|
||||
<option value="">
|
||||
@@ -262,9 +292,14 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
|
||||
<button v-if="!isPieceLocked(index)" type="button" class="btn btn-error btn-xs btn-square" @click="removePiece(index)">
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
<div v-else class="tooltip tooltip-left" data-tip="Cette pièce ne peut pas être supprimée">
|
||||
<button type="button" class="btn btn-ghost btn-xs btn-square opacity-30 cursor-not-allowed" disabled>
|
||||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -274,7 +309,7 @@
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<h4 :class="headingClass">Sous-composants</h4>
|
||||
<button
|
||||
v-if="canManageSubcomponents"
|
||||
v-if="canManageSubcomponents && !restrictedMode"
|
||||
type="button"
|
||||
class="btn btn-outline btn-xs"
|
||||
@click="addSubComponent"
|
||||
@@ -317,6 +352,8 @@
|
||||
:product-types="productTypes"
|
||||
:allow-subcomponents="childAllowSubcomponents"
|
||||
:max-subcomponent-depth="maxSubcomponentDepth"
|
||||
:restricted-mode="restrictedMode"
|
||||
:is-locked="isSubcomponentLocked(index)"
|
||||
@remove="removeSubComponent(index)"
|
||||
/>
|
||||
</div>
|
||||
@@ -359,6 +396,8 @@ const props = withDefaults(defineProps<{
|
||||
lockedTypeLabel?: string
|
||||
allowSubcomponents?: boolean
|
||||
maxSubcomponentDepth?: number
|
||||
restrictedMode?: boolean
|
||||
isLocked?: boolean
|
||||
}>(), {
|
||||
depth: 0,
|
||||
componentTypes: () => [],
|
||||
@@ -369,10 +408,52 @@ const props = withDefaults(defineProps<{
|
||||
lockedTypeLabel: '',
|
||||
allowSubcomponents: true,
|
||||
maxSubcomponentDepth: Infinity,
|
||||
restrictedMode: false,
|
||||
isLocked: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['remove'])
|
||||
|
||||
const initialCustomFieldIndices = ref<Set<number>>(new Set())
|
||||
const initialPieceIndices = ref<Set<number>>(new Set())
|
||||
const initialProductIndices = ref<Set<number>>(new Set())
|
||||
const initialSubcomponentIndices = ref<Set<number>>(new Set())
|
||||
|
||||
const initializeLockedIndices = () => {
|
||||
if (props.restrictedMode) {
|
||||
const customFieldsLength = Array.isArray(props.node.customFields) ? props.node.customFields.length : 0
|
||||
const piecesLength = Array.isArray(props.node.pieces) ? props.node.pieces.length : 0
|
||||
const productsLength = Array.isArray(props.node.products) ? props.node.products.length : 0
|
||||
const subcomponentsLength = Array.isArray(props.node.subcomponents) ? props.node.subcomponents.length : 0
|
||||
|
||||
initialCustomFieldIndices.value = new Set(Array.from({ length: customFieldsLength }, (_, i) => i))
|
||||
initialPieceIndices.value = new Set(Array.from({ length: piecesLength }, (_, i) => i))
|
||||
initialProductIndices.value = new Set(Array.from({ length: productsLength }, (_, i) => i))
|
||||
initialSubcomponentIndices.value = new Set(Array.from({ length: subcomponentsLength }, (_, i) => i))
|
||||
}
|
||||
}
|
||||
|
||||
initializeLockedIndices()
|
||||
|
||||
const isCustomFieldLocked = (index: number): boolean => {
|
||||
return props.restrictedMode === true && initialCustomFieldIndices.value.has(index)
|
||||
}
|
||||
|
||||
const isPieceLocked = (index: number): boolean => {
|
||||
return props.restrictedMode === true && initialPieceIndices.value.has(index)
|
||||
}
|
||||
|
||||
const isProductLocked = (index: number): boolean => {
|
||||
return props.restrictedMode === true && initialProductIndices.value.has(index)
|
||||
}
|
||||
|
||||
const isSubcomponentLocked = (index: number): boolean => {
|
||||
return props.restrictedMode === true && initialSubcomponentIndices.value.has(index)
|
||||
}
|
||||
|
||||
const isLocked = computed(() => props.isLocked === true)
|
||||
const restrictedMode = computed(() => props.restrictedMode === true)
|
||||
|
||||
const componentTypes = computed(() => props.componentTypes ?? [])
|
||||
const pieceTypes = computed(() => props.pieceTypes ?? [])
|
||||
const productTypes = computed(() => props.productTypes ?? [])
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
minlength="2"
|
||||
maxlength="120"
|
||||
required
|
||||
:disabled="restrictedMode"
|
||||
/>
|
||||
<p v-if="errors.name" class="mt-1 text-sm text-error">{{ errors.name }}</p>
|
||||
</div>
|
||||
@@ -47,6 +48,7 @@
|
||||
rows="4"
|
||||
name="notes"
|
||||
maxlength="2000"
|
||||
:disabled="restrictedMode"
|
||||
></textarea>
|
||||
<p class="mt-1 text-xs text-base-content/70">Saisissez des informations complémentaires (facultatif).</p>
|
||||
</div>
|
||||
@@ -81,6 +83,7 @@
|
||||
v-model="componentStructure"
|
||||
:allow-subcomponents="allowComponentSubcomponents"
|
||||
:max-subcomponent-depth="componentSubcomponentMaxDepth"
|
||||
:restricted-mode="restrictedMode"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -92,7 +95,7 @@
|
||||
Aperçu :
|
||||
<span class="font-medium text-base-content">{{ pieceStructurePreview }}</span>
|
||||
</p>
|
||||
<PieceModelStructureEditor v-model="pieceStructure" />
|
||||
<PieceModelStructureEditor v-model="pieceStructure" :restricted-mode="restrictedMode" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -103,11 +106,21 @@
|
||||
Aperçu :
|
||||
<span class="font-medium text-base-content">{{ productStructurePreview }}</span>
|
||||
</p>
|
||||
<PieceModelStructureEditor v-model="productStructure" />
|
||||
<PieceModelStructureEditor v-model="productStructure" :restricted-mode="restrictedMode" />
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<div
|
||||
v-if="restrictedMode && restrictedModeMessage"
|
||||
class="alert alert-info"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
<span>{{ restrictedModeMessage }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="disableSubmit"
|
||||
class="alert alert-warning"
|
||||
@@ -161,6 +174,8 @@ const props = withDefaults(defineProps<{
|
||||
componentSubcomponentMaxDepth?: number
|
||||
disableSubmit?: boolean
|
||||
disableSubmitMessage?: string
|
||||
restrictedMode?: boolean
|
||||
restrictedModeMessage?: string
|
||||
}>(), {
|
||||
initialData: null,
|
||||
saving: false,
|
||||
@@ -170,6 +185,8 @@ const props = withDefaults(defineProps<{
|
||||
componentSubcomponentMaxDepth: 1,
|
||||
disableSubmit: false,
|
||||
disableSubmitMessage: '',
|
||||
restrictedMode: false,
|
||||
restrictedModeMessage: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -192,6 +209,12 @@ const disableSubmitMessage = computed(() =>
|
||||
? props.disableSubmitMessage
|
||||
: 'Cette catégorie ne peut pas être modifiée car des éléments y sont déjà liés.',
|
||||
)
|
||||
const restrictedMode = computed(() => props.restrictedMode === true)
|
||||
const restrictedModeMessage = computed(() =>
|
||||
(props.restrictedModeMessage && props.restrictedModeMessage.trim())
|
||||
? props.restrictedModeMessage
|
||||
: '',
|
||||
)
|
||||
|
||||
const form = reactive<ModelTypePayload>({
|
||||
name: '',
|
||||
|
||||
@@ -32,7 +32,7 @@ const extractTotal = (payload: any, fallbackLength: number) => {
|
||||
|
||||
export function useCategoryEditGuard (config: GuardConfig) {
|
||||
const { get } = useApi()
|
||||
const { showError } = useToast()
|
||||
const { showInfo } = useToast()
|
||||
|
||||
const linkedCount = ref(0)
|
||||
const linkedLoading = ref(false)
|
||||
@@ -64,11 +64,15 @@ export function useCategoryEditGuard (config: GuardConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
const isSubmitBlocked = computed(
|
||||
() => linkedLoading.value || linkedCount.value > 0,
|
||||
const isRestrictedMode = computed(
|
||||
() => !linkedLoading.value && linkedCount.value > 0,
|
||||
)
|
||||
|
||||
const submitBlockMessage = computed(() => {
|
||||
const isSubmitBlocked = computed(
|
||||
() => linkedLoading.value,
|
||||
)
|
||||
|
||||
const restrictedModeMessage = computed(() => {
|
||||
if (linkedLoading.value) {
|
||||
return config.labels.verifying
|
||||
}
|
||||
@@ -76,23 +80,32 @@ export function useCategoryEditGuard (config: GuardConfig) {
|
||||
return ''
|
||||
}
|
||||
if (linkedCount.value === 1) {
|
||||
return `Modification bloquée : 1 ${config.labels.singular} est déjà lié à cette catégorie.`
|
||||
return `Mode restreint : 1 ${config.labels.singular} est déjà lié à cette catégorie. Vous pouvez ajouter de nouveaux champs personnalisés, mais pas modifier ou supprimer les existants.`
|
||||
}
|
||||
return `Modification bloquée : ${linkedCount.value} ${config.labels.plural} sont déjà liés à cette catégorie.`
|
||||
return `Mode restreint : ${linkedCount.value} ${config.labels.plural} sont déjà liés à cette catégorie. Vous pouvez ajouter de nouveaux champs personnalisés, mais pas modifier ou supprimer les existants.`
|
||||
})
|
||||
|
||||
const submitBlockMessage = computed(() => {
|
||||
if (linkedLoading.value) {
|
||||
return config.labels.verifying
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const guardSubmitOrNotify = () => {
|
||||
if (!isSubmitBlocked.value) {
|
||||
return false
|
||||
}
|
||||
showError(submitBlockMessage.value || 'Modification bloquée pour cette catégorie.')
|
||||
showInfo(submitBlockMessage.value || 'Veuillez patienter...')
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
linkedCount,
|
||||
linkedLoading,
|
||||
isRestrictedMode,
|
||||
isSubmitBlocked,
|
||||
restrictedModeMessage,
|
||||
submitBlockMessage,
|
||||
loadLinkedCount,
|
||||
guardSubmitOrNotify,
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
:saving="saving"
|
||||
:disable-submit="isSubmitBlocked"
|
||||
:disable-submit-message="submitBlockMessage"
|
||||
:restricted-mode="isRestrictedMode"
|
||||
:restricted-mode-message="restrictedModeMessage"
|
||||
@submit="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
/>
|
||||
@@ -52,7 +54,9 @@ const saving = ref(false)
|
||||
const initialData = ref<Partial<ModelTypePayload> | null>(null)
|
||||
|
||||
const {
|
||||
isRestrictedMode,
|
||||
isSubmitBlocked,
|
||||
restrictedModeMessage,
|
||||
submitBlockMessage,
|
||||
loadLinkedCount,
|
||||
guardSubmitOrNotify,
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
:saving="saving"
|
||||
:disable-submit="isSubmitBlocked"
|
||||
:disable-submit-message="submitBlockMessage"
|
||||
:restricted-mode="isRestrictedMode"
|
||||
:restricted-mode-message="restrictedModeMessage"
|
||||
@submit="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
/>
|
||||
@@ -52,7 +54,9 @@ const saving = ref(false)
|
||||
const initialData = ref<Partial<ModelTypePayload> | null>(null)
|
||||
|
||||
const {
|
||||
isRestrictedMode,
|
||||
isSubmitBlocked,
|
||||
restrictedModeMessage,
|
||||
submitBlockMessage,
|
||||
loadLinkedCount,
|
||||
guardSubmitOrNotify,
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
:saving="saving"
|
||||
:disable-submit="isSubmitBlocked"
|
||||
:disable-submit-message="submitBlockMessage"
|
||||
:restricted-mode="isRestrictedMode"
|
||||
:restricted-mode-message="restrictedModeMessage"
|
||||
@submit="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
/>
|
||||
@@ -52,7 +54,9 @@ const saving = ref(false)
|
||||
const initialData = ref<Partial<ModelTypePayload> | null>(null)
|
||||
|
||||
const {
|
||||
isRestrictedMode,
|
||||
isSubmitBlocked,
|
||||
restrictedModeMessage,
|
||||
submitBlockMessage,
|
||||
loadLinkedCount,
|
||||
guardSubmitOrNotify,
|
||||
|
||||
Reference in New Issue
Block a user