This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

272
ui/src/pages/UserList.vue Normal file
View File

@@ -0,0 +1,272 @@
<template>
<q-page class="ol-page with-bg">
<!-- 🔍 Sticky Filter -->
<div class="ol-filter-bar">
<div class="ol-filter-row">
<q-input
class="ol-filter-input ol-search"
dense
filled
clearable
v-model="store.filters.search"
label="Arama (Kullanıcı / Rol / Piyasa)"
debounce="300"
@update:model-value="store.fetchUsers"
>
<template #append>
<q-icon name="search" />
</template>
</q-input>
<q-toggle
v-model="store.filters.onlyActive"
label="Sadece Aktifler"
/>
<div class="ol-filter-actions">
<q-btn
label="Yenile"
icon="refresh"
color="primary"
:loading="store.loading"
@click="store.fetchUsers"
/>
<q-btn
label="Yeni Kullanıcı"
icon="person_add"
color="primary"
outline
@click="goCreate"
/>
</div>
</div>
</div>
<!-- 📋 USER LIST TABLE -->
<q-table
title="Mevcut Kullanıcılar"
class="ol-table"
flat
bordered
dense
separator="cell"
row-key="id"
:rows="store.filteredUsers"
:columns="columns"
:loading="store.loading"
no-data-label="Kullanıcı bulunamadı"
:rows-per-page-options="[0]"
hide-bottom
>
<!-- 🔗 OPEN -->
<template #body-cell-open="props">
<q-td class="text-center">
<q-btn
icon="open_in_new"
color="primary"
flat
round
dense
@click="openDetail(props.row.id)"
/>
</q-td>
</template>
<!-- DURUM -->
<template #body-cell-is_active="props">
<q-td class="text-center">
<q-icon
:name="props.row.is_active ? 'check_circle' : 'cancel'"
:color="props.row.is_active ? 'green' : 'red'"
size="18px"
/>
</q-td>
</template>
<!-- 👤 ROLLER -->
<template #body-cell-role_names="props">
<q-td>
<q-chip
v-for="r in splitNames(props.row.role_names)"
:key="r"
dense
color="primary"
text-color="white"
class="q-mr-xs"
>
{{ r }}
</q-chip>
</q-td>
</template>
<!-- 🏢 DEPARTMAN -->
<template #body-cell-department_names="props">
<q-td>
<q-chip
v-for="d in splitNames(props.row.department_names)"
:key="d"
dense
color="grey-7"
text-color="white"
class="q-mr-xs"
>
{{ d }}
</q-chip>
</q-td>
</template>
<!-- 🌍 PİYASALAR -->
<template #body-cell-piyasa_names="props">
<q-td class="ol-col-piyasa">
<div class="piyasa-wrap">
<q-chip
v-for="p in splitPiyasalar(props.row.piyasa_names)"
:key="p"
dense
outline
color="indigo"
class="piyasa-chip"
:title="p"
>
{{ p }}
</q-chip>
</div>
</q-td>
</template>
</q-table>
<!-- HATA -->
<q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
{{ store.error }}
</q-banner>
</q-page>
</template>
<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useUserListStore } from 'src/stores/UserListStore'
import { usePermission } from 'src/composables/usePermission'
const { canRead, canWrite, canUpdate } = usePermission()
const canReadOrder = canRead('order')
const canWriteOrder = canWrite('order')
const canUpdateOrder = canUpdate('order')
const router = useRouter()
const store = useUserListStore()
/* ==========================================================
📌 QTable Columns
========================================================== */
const columns = [
{ name: 'open', label: '', align: 'center' },
{
name: 'id',
label: 'No',
field: row => row.id,
sortable: true
},
{
name: 'code',
label: 'Kullanıcı',
field: row => row.code || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr', { sensitivity: 'base' })
},
{
name: 'nebim_username',
label: 'Nebim',
field: row => row.nebim_username || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr')
},
{
name: 'user_group_code',
label: 'Grup',
field: row => row.user_group_code || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr')
},
{
name: 'is_active',
label: 'Durum',
field: row => row.is_active,
align: 'center',
sortable: true,
sort: (a, b) => Number(b) - Number(a)
},
{
name: 'role_names',
label: 'Roller',
field: row => row.role_names || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr')
},
{
name: 'department_names',
label: 'Departmanlar',
field: row => row.department_names || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr')
},
{
name: 'piyasa_names',
label: 'Piyasalar',
field: row => row.piyasa_names || '',
sortable: true,
sort: (a, b) => a.localeCompare(b, 'tr')
}
]
/* ==========================================================
HELPERS
========================================================== */
function splitNames(val) {
if (!val) return []
return val.split(',').map(v => v.trim())
}
function openDetail(id) {
router.push({
path: `/app/users/edit/${id}`
})
}
function goCreate() {
router.push({ name: 'user-new' })
}
function splitPiyasalar (val) {
if (!val) return []
return val
.split(',')
.map(v => v.trim())
.filter(Boolean)
.slice(0, 24) // ✅ 6 satır × 4 kolon
}
onMounted(store.fetchUsers)
</script>