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>
115 lines
3.0 KiB
TypeScript
115 lines
3.0 KiB
TypeScript
import { computed, ref } from 'vue'
|
|
import { useApi } from '~/composables/useApi'
|
|
import { useToast } from '~/composables/useToast'
|
|
|
|
type GuardLabels = {
|
|
singular: string
|
|
plural: string
|
|
verifying: string
|
|
}
|
|
|
|
type GuardConfig = {
|
|
endpoint: string
|
|
filterKey: string
|
|
labels: GuardLabels
|
|
}
|
|
|
|
const extractTotal = (payload: any, fallbackLength: number) => {
|
|
if (typeof payload?.totalItems === 'number') {
|
|
return payload.totalItems
|
|
}
|
|
if (typeof payload?.['hydra:totalItems'] === 'number') {
|
|
return payload['hydra:totalItems']
|
|
}
|
|
if (Array.isArray(payload?.member)) {
|
|
return payload.member.length
|
|
}
|
|
if (Array.isArray(payload?.['hydra:member'])) {
|
|
return payload['hydra:member'].length
|
|
}
|
|
return fallbackLength
|
|
}
|
|
|
|
export function useCategoryEditGuard (config: GuardConfig) {
|
|
const { get } = useApi()
|
|
const { showInfo } = useToast()
|
|
|
|
const linkedCount = ref(0)
|
|
const linkedLoading = ref(false)
|
|
|
|
const loadLinkedCount = async (modelTypeId: string) => {
|
|
linkedLoading.value = true
|
|
try {
|
|
const params = new URLSearchParams()
|
|
params.set('itemsPerPage', '1')
|
|
params.set(config.filterKey, `/api/model_types/${modelTypeId}`)
|
|
|
|
const result = await get(`${config.endpoint}?${params.toString()}`)
|
|
if (!result.success) {
|
|
linkedCount.value = 0
|
|
return
|
|
}
|
|
|
|
const fallbackLength = Array.isArray(result.data?.member)
|
|
? result.data.member.length
|
|
: Array.isArray(result.data?.['hydra:member'])
|
|
? result.data['hydra:member'].length
|
|
: 0
|
|
|
|
linkedCount.value = extractTotal(result.data, fallbackLength)
|
|
} catch (error) {
|
|
linkedCount.value = 0
|
|
} finally {
|
|
linkedLoading.value = false
|
|
}
|
|
}
|
|
|
|
const isRestrictedMode = computed(
|
|
() => !linkedLoading.value && linkedCount.value > 0,
|
|
)
|
|
|
|
const isSubmitBlocked = computed(
|
|
() => linkedLoading.value,
|
|
)
|
|
|
|
const restrictedModeMessage = computed(() => {
|
|
if (linkedLoading.value) {
|
|
return config.labels.verifying
|
|
}
|
|
if (linkedCount.value <= 0) {
|
|
return ''
|
|
}
|
|
if (linkedCount.value === 1) {
|
|
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 `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
|
|
}
|
|
showInfo(submitBlockMessage.value || 'Veuillez patienter...')
|
|
return true
|
|
}
|
|
|
|
return {
|
|
linkedCount,
|
|
linkedLoading,
|
|
isRestrictedMode,
|
|
isSubmitBlocked,
|
|
restrictedModeMessage,
|
|
submitBlockMessage,
|
|
loadLinkedCount,
|
|
guardSubmitOrNotify,
|
|
}
|
|
}
|
|
|