ilk
This commit is contained in:
435
ui/src/pages/RoleDepartmentPermissionPage.vue
Normal file
435
ui/src/pages/RoleDepartmentPermissionPage.vue
Normal file
@@ -0,0 +1,435 @@
|
||||
<template>
|
||||
<div v-if="!lookupsLoaded" class="q-pa-xl flex flex-center">
|
||||
|
||||
<q-spinner
|
||||
color="primary"
|
||||
size="48px"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<q-page class="permissions-page">
|
||||
|
||||
<!-- ================= STICKY STACK ================= -->
|
||||
<div class="sticky-stack">
|
||||
|
||||
<!-- FILTER BAR -->
|
||||
<div
|
||||
v-if="lookupsLoaded"
|
||||
class="filter-bar row q-col-gutter-md"
|
||||
>
|
||||
|
||||
|
||||
<div class="col-4">
|
||||
<q-select
|
||||
v-model="roleId"
|
||||
:options="roles"
|
||||
option-value="id"
|
||||
option-label="title"
|
||||
emit-value
|
||||
map-options
|
||||
label="Rol"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="loadMatrix"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<q-select
|
||||
v-model="deptCode"
|
||||
:options="departments"
|
||||
option-value="id"
|
||||
option-label="title"
|
||||
emit-value
|
||||
map-options
|
||||
label="Departman"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="loadMatrix"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- SAVE TOOLBAR -->
|
||||
<div class="save-toolbar">
|
||||
|
||||
<div class="label">
|
||||
Rol + Departman Yetkilendirme
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
color="primary"
|
||||
icon="save"
|
||||
label="Kaydet"
|
||||
:disable="!dirty"
|
||||
@click="save"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ================= TABLE SCROLL ================= -->
|
||||
|
||||
<div
|
||||
v-if="lookupsLoaded"
|
||||
class="permissions-table-scroll"
|
||||
>
|
||||
|
||||
|
||||
<q-table
|
||||
class="permissions-table"
|
||||
:rows="rows"
|
||||
:columns="columns"
|
||||
row-key="module"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
:loading="loading"
|
||||
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
>
|
||||
|
||||
<!-- ========== HEADER ========== -->
|
||||
<template v-slot:header-cell="props">
|
||||
|
||||
<q-th :props="props">
|
||||
|
||||
<!-- Module başlığı -->
|
||||
<span v-if="props.col.name === 'module'">
|
||||
{{ props.col.label }}
|
||||
</span>
|
||||
|
||||
<!-- Checkbox kolon başlığı -->
|
||||
<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"
|
||||
:class="props.col.name === 'module'
|
||||
? 'permissions-sticky-col'
|
||||
: ''"
|
||||
>
|
||||
|
||||
<!-- Module adı -->
|
||||
<span v-if="props.col.name === 'module'">
|
||||
{{ props.row.label }}
|
||||
</span>
|
||||
|
||||
<!-- Checkbox -->
|
||||
<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>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { Notify } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
|
||||
/* ================= STATE ================= */
|
||||
|
||||
const roles = ref([])
|
||||
const departments = ref([])
|
||||
|
||||
const roleId = ref(null)
|
||||
const deptCode = ref(null)
|
||||
|
||||
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ı' }
|
||||
]
|
||||
|
||||
|
||||
/* ================= MODULES ================= */
|
||||
|
||||
const modules = ref([])
|
||||
|
||||
|
||||
/* ================= TABLE ================= */
|
||||
|
||||
const columns = [
|
||||
|
||||
{
|
||||
name: 'module',
|
||||
label: 'Modül',
|
||||
field: 'label',
|
||||
align: 'left'
|
||||
},
|
||||
|
||||
...actions.map(a => ({
|
||||
name: a.key,
|
||||
label: a.label,
|
||||
align: 'center'
|
||||
}))
|
||||
]
|
||||
|
||||
let matrixLoading = false
|
||||
|
||||
|
||||
/* ================= LOOKUPS ================= */
|
||||
|
||||
async function loadLookups () {
|
||||
|
||||
const [r, d, m] = await Promise.all([
|
||||
api.get('/lookups/roles-perm'),
|
||||
api.get('/lookups/departments-perm'),
|
||||
api.get('/lookups/modules')
|
||||
])
|
||||
|
||||
roles.value = r.data || []
|
||||
departments.value = d.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 (!roleId.value || !deptCode.value) return
|
||||
if (matrixLoading) return
|
||||
|
||||
matrixLoading = true
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
|
||||
if (!modules.value.length) {
|
||||
await loadLookups()
|
||||
}
|
||||
|
||||
initMatrix()
|
||||
|
||||
const res = await api.get(
|
||||
`/roles/${roleId.value}/departments/${deptCode.value}/permissions`
|
||||
)
|
||||
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
|
||||
console.log('PERM LIST:', list.slice(0, 10))
|
||||
|
||||
|
||||
// ✅ BACKEND → UI ACTION 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 (err) {
|
||||
|
||||
console.error('PERM LOAD ERROR:', err)
|
||||
|
||||
Notify.create({
|
||||
type: 'negative',
|
||||
message: 'Yetkiler yüklenemedi'
|
||||
})
|
||||
|
||||
} finally {
|
||||
|
||||
loading.value = false
|
||||
matrixLoading = 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(
|
||||
`/roles/${roleId.value}/departments/${deptCode.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()
|
||||
})
|
||||
|
||||
watch(roleId, v => console.log('ROLE_ID >>>', v))
|
||||
watch(deptCode, v => console.log('DEPT >>>', v))
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user