DispatchPage.vue: 1320→1217 lines - Extract SbModal.vue + SbContextMenu.vue reusable components - Extract useAbsenceResize composable - Extract dispatch constants to config/dispatch.js ProjectWizard.vue: 1185→673 lines (-43%) - Extract useWizardPublish composable (270-line publish function) - Extract useWizardCatalog composable - Extract wizard-constants.js (step labels, options, categories) SettingsPage.vue: 1172→850 lines (-27%) - Extract usePermissionMatrix composable - Extract useUserGroups composable - Extract useLegacySync composable ClientDetailPage.vue: 1169→864 lines (-26%) - Extract useClientData composable (loadCustomer broken into sub-functions) - Extract useEquipmentActions composable - Extract client-constants.js + erp-pdf.js utility checkout.js: 639→408 lines (-36%) - Extract address-search.js module - Extract otp.js module - Extract email-templates.js module - Extract project-templates.js module - Add erpQuery() helper to DRY repeated URL construction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
3.9 KiB
JavaScript
124 lines
3.9 KiB
JavaScript
import { ref, reactive, computed } from 'vue'
|
|
import { Notify } from 'quasar'
|
|
import { usePermissions } from './usePermissions'
|
|
|
|
export function usePermissionMatrix () {
|
|
const { HUB_URL } = usePermissions()
|
|
|
|
const permTab = ref('users')
|
|
const permLoading = ref(false)
|
|
const permSaving = ref(false)
|
|
const permDirty = ref(false)
|
|
const permGroups = ref([])
|
|
const permCapabilities = ref([])
|
|
const permMatrix = reactive({})
|
|
|
|
const allGroupNames = computed(() => permGroups.value.map(g => g.name))
|
|
const permCategories = computed(() => [...new Set(permCapabilities.value.map(c => c.category))])
|
|
const permCapsByCategory = computed(() => {
|
|
const map = {}
|
|
for (const c of permCapabilities.value) {
|
|
if (!map[c.category]) map[c.category] = []
|
|
map[c.category].push(c)
|
|
}
|
|
return map
|
|
})
|
|
|
|
async function loadPerms () {
|
|
permLoading.value = true
|
|
try {
|
|
const res = await fetch(HUB_URL + '/auth/groups')
|
|
const data = await res.json()
|
|
permGroups.value = data.groups
|
|
permCapabilities.value = data.capabilities
|
|
for (const g of data.groups) {
|
|
permMatrix[g.name] = {}
|
|
for (const cap of data.capabilities) {
|
|
permMatrix[g.name][cap.key] = g.permissions[cap.key] === true
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: 'Erreur chargement permissions: ' + e.message })
|
|
} finally {
|
|
permLoading.value = false
|
|
}
|
|
}
|
|
|
|
async function saveAllPerms () {
|
|
permSaving.value = true
|
|
try {
|
|
for (const g of permGroups.value) {
|
|
await fetch(HUB_URL + '/auth/groups/' + encodeURIComponent(g.name) + '/permissions', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(permMatrix[g.name]),
|
|
})
|
|
}
|
|
permDirty.value = false
|
|
Notify.create({ type: 'positive', message: 'Permissions sauvegardees', timeout: 2000 })
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
|
|
} finally {
|
|
permSaving.value = false
|
|
}
|
|
}
|
|
|
|
async function saveGroupPerms (groupName) {
|
|
permSaving.value = true
|
|
try {
|
|
await fetch(HUB_URL + '/auth/groups/' + encodeURIComponent(groupName) + '/permissions', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(permMatrix[groupName]),
|
|
})
|
|
permDirty.value = false
|
|
Notify.create({ type: 'positive', message: `Permissions "${groupName}" sauvegardees`, timeout: 2000 })
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: 'Erreur: ' + e.message })
|
|
} finally {
|
|
permSaving.value = false
|
|
}
|
|
}
|
|
|
|
function countGroupPerms (g) {
|
|
return Object.values(permMatrix[g.name] || {}).filter(Boolean).length
|
|
}
|
|
|
|
function effectivePerm (user, capKey) {
|
|
if (typeof user.overrides[capKey] === 'boolean') return user.overrides[capKey]
|
|
for (const gName of user.groups) {
|
|
if (permMatrix[gName]?.[capKey]) return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
async function setUserOverride (user, capKey, value) {
|
|
if (value === true || value === false) {
|
|
user.overrides[capKey] = value
|
|
} else {
|
|
delete user.overrides[capKey]
|
|
}
|
|
const clean = {}
|
|
for (const [k, v] of Object.entries(user.overrides)) {
|
|
if (typeof v === 'boolean') clean[k] = v
|
|
}
|
|
try {
|
|
await fetch(HUB_URL + '/auth/users/' + encodeURIComponent(user.email) + '/overrides', {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(clean),
|
|
})
|
|
} catch (e) {
|
|
Notify.create({ type: 'negative', message: 'Erreur override: ' + e.message })
|
|
}
|
|
}
|
|
|
|
return {
|
|
permTab, permLoading, permSaving, permDirty,
|
|
permGroups, permCapabilities, permMatrix,
|
|
allGroupNames, permCategories, permCapsByCategory,
|
|
loadPerms, saveAllPerms, saveGroupPerms, countGroupPerms,
|
|
effectivePerm, setUserOverride,
|
|
}
|
|
}
|