Files
bssapp/ui/src/pages/UserPermissionPage.vue

362 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div v-if="canUpdateUser && !lookupsLoaded" class="q-pa-xl flex flex-center">
<q-spinner color="primary" size="48px" />
</div>
<q-page v-if="canUpdateUser" class="permissions-page">
<div class="sticky-stack">
<!-- USER SELECT -->
<div v-if="lookupsLoaded" class="filter-bar row q-col-gutter-md">
<div class="col-4">
<q-select
v-model="userId"
:options="users"
option-value="id"
option-label="title"
emit-value
map-options
label="Kullanıcı"
dense
outlined
@update:model-value="loadMatrix"
/>
</div>
</div>
<!-- SAVE -->
<div class="save-toolbar">
<div class="label">
Kullanıcı Override Yetkileri
</div>
<q-btn
v-if="canUpdateUser"
color="primary"
icon="save"
label="Kaydet"
:disable="!dirty"
@click="save"
/>
</div>
</div>
<!-- TABLE -->
<div v-if="lookupsLoaded" class="permissions-table-scroll">
<q-table
:rows="rows"
:columns="columns"
row-key="module"
dense
bordered
flat
:loading="loading"
:pagination="{ rowsPerPage: 0 }"
>
<!-- HEADER -->
<template v-slot:header-cell="props">
<q-th :props="props">
<span v-if="props.col.name === 'module'">
{{ props.col.label }}
</span>
<div v-else class="column items-center">
<span class="text-caption">
{{ props.col.label }}
</span>
<q-checkbox
dense
:model-value="isColumnChecked(props.col.name)"
@update:model-value="toggleColumn(props.col.name, $event)"
/>
</div>
</q-th>
</template>
<!-- BODY -->
<template v-slot:body-cell="props">
<q-td :props="props">
<span v-if="props.col.name === 'module'">
{{ props.row.label }}
</span>
<q-checkbox
v-else
v-model="props.row[props.col.name]"
dense
@update:model-value="dirty = true"
/>
</q-td>
</template>
</q-table>
</div>
</q-page>
<q-page v-else class="q-pa-md flex flex-center">
<div class="text-negative text-subtitle1">
Bu module erisim yetkiniz yok.
</div>
</q-page>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Notify } from 'quasar'
import api from 'src/services/api'
import { usePermission } from 'src/composables/usePermission'
const { canUpdate } = usePermission()
const canUpdateUser = canUpdate('user')
/* ================= STATE ================= */
const users = ref([])
const userId = ref(null)
const modules = ref([])
const rows = ref([])
const loading = ref(false)
const dirty = ref(false)
const lookupsLoaded = ref(false)
/* ================= ACTIONS ================= */
const actions = [
{ key: 'write', label: 'Ekleme' },
{ key: 'read', label: 'Görüntüleme' },
{ key: 'delete', label: 'Silme' },
{ key: 'update', label: 'Güncelleme' },
{ key: 'export', label: ıktı' }
]
/* ================= TABLE ================= */
const columns = [
{
name: 'module',
label: 'Modül',
field: 'label',
align: 'left'
},
...actions.map(a => ({
name: a.key,
label: a.label,
align: 'center'
}))
]
/* ================= LOOKUPS ================= */
async function loadLookups () {
const [u, m] = await Promise.all([
api.get('/lookups/users-perm'),
api.get('/lookups/modules')
])
users.value = u.data || []
modules.value = m.data || []
lookupsLoaded.value = true
}
/* ================= INIT MATRIX ================= */
function initMatrix () {
rows.value = modules.value.map(m => {
const row = {
module: String(m.value).toLowerCase().trim(),
label: m.label
}
actions.forEach(a => {
row[a.key] = false
})
return row
})
}
/* ================= LOAD ================= */
async function loadMatrix () {
if (!userId.value) return
loading.value = true
try {
initMatrix()
const res = await api.get(
`/users/${userId.value}/permissions`
)
const list = Array.isArray(res.data) ? res.data : []
// ✅ BACKEND → UI MAP
const actionMap = {
insert: 'write',
view: 'read',
delete: 'delete',
update: 'update',
export: 'export'
}
list.forEach(p => {
const code = String(p.module_code || p.module)
.toLowerCase()
.trim()
const rawAction = String(p.action)
.toLowerCase()
.trim()
const mappedAction = actionMap[rawAction] || rawAction
const row = rows.value.find(r => r.module === code)
if (row && row.hasOwnProperty(mappedAction)) {
row[mappedAction] = Boolean(p.allowed)
}
})
dirty.value = false
} catch (e) {
console.error('PERM LOAD ERROR:', e)
Notify.create({
type: 'negative',
message: 'Yükleme hatası'
})
} finally {
loading.value = false
}
}
/* ================= SAVE ================= */
async function save () {
try {
loading.value = true
const payload = []
rows.value.forEach(r => {
actions.forEach(a => {
payload.push({
module: r.module,
action: a.key,
allowed: r[a.key]
})
})
})
await api.post(
`/users/${userId.value}/permissions`,
payload
)
Notify.create({
type: 'positive',
message: 'Kaydedildi'
})
dirty.value = false
} catch {
Notify.create({
type: 'negative',
message: 'Kayıt hatası'
})
} finally {
loading.value = false
}
}
/* ================= COLUMN ================= */
function isColumnChecked (key) {
if (!rows.value.length) return false
return rows.value.every(r => r[key] === true)
}
function toggleColumn (key, val) {
rows.value.forEach(r => {
r[key] = val
})
dirty.value = true
}
/* ================= INIT ================= */
onMounted(() => {
loadLookups()
})
</script>