mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 11:23:18 +00:00
Implementa el módulo integral de costos según el plan acordado: - Esquema Liquibase: expense_entries, almacén (central + obra), IVA, tenant_cost_settings, permisos IAM y RLS - Funciones RPC core: gastos, almacén, control presupuestal, sync nómina, cierre de almacén al concluir proyecto - API HTTP: expenses_http, warehouse_http, cost_control_http, iam_http - Middleware requirePermission con matriz IAM - UI web-panel: /gastos, /almacen, /control-presupuesto, /usuarios - Componentes BudgetItemPicker, CostSemaphore y semáforos CSS - Smoke tests extendidos para gastos/almacén/control Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
78 lines
2.8 KiB
Vue
78 lines
2.8 KiB
Vue
<template>
|
|
<div class="usuarios-page">
|
|
<DeskTableBar v-model="tableQ" placeholder="Buscar usuario">
|
|
<Chip :label="`${users.length} usuarios`" icon="pi pi-users" />
|
|
</DeskTableBar>
|
|
|
|
<DataTable :value="listed" data-key="id" paginator :rows="25" scrollable scroll-height="flex">
|
|
<Column field="username" header="Usuario" style="width: 10rem" />
|
|
<Column field="display_name" header="Nombre" />
|
|
<Column field="role_code" header="Rol" style="width: 9rem" />
|
|
<Column field="email" header="Correo" style="width: 14rem" />
|
|
</DataTable>
|
|
|
|
<Panel header="Permisos del rol operativo (user)" class="mt-4">
|
|
<div class="perm-grid">
|
|
<div v-for="perm in permissions" :key="perm.code" class="perm-item">
|
|
<Checkbox v-model="rolePerms" :input-id="perm.code" :value="perm.code" />
|
|
<label :for="perm.code">{{ perm.label }}</label>
|
|
</div>
|
|
</div>
|
|
<Button label="Guardar permisos" icon="pi pi-save" class="mt-3" @click="saveRolePerms" />
|
|
</Panel>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { displayErrorMessage } from "~/composables/api-response";
|
|
|
|
type UserRow = { id: number; username: string; display_name: string; role_code: string; email: string };
|
|
type PermRow = { code: string; label: string };
|
|
|
|
const desk = useDesktop();
|
|
const { api } = useApi();
|
|
const toast = useToast();
|
|
|
|
const tableQ = ref("");
|
|
const users = ref<UserRow[]>([]);
|
|
const permissions = ref<PermRow[]>([]);
|
|
const rolePerms = ref<string[]>([]);
|
|
|
|
const listed = computed(() => {
|
|
const q = tableQ.value.toLowerCase();
|
|
return users.value.filter((u) => !q || [u.username, u.display_name, u.email].join(" ").toLowerCase().includes(q));
|
|
});
|
|
|
|
async function load() {
|
|
const [u, p, rp] = await Promise.all([
|
|
api<{ data?: { users?: UserRow[] } }>("/v1/iam/users"),
|
|
api<{ data?: { permissions?: PermRow[] } }>("/v1/iam/permissions"),
|
|
api<{ data?: { permissions?: string[] } }>("/v1/iam/roles/user/permissions"),
|
|
]);
|
|
users.value = u.data?.users ?? [];
|
|
permissions.value = p.data?.permissions ?? [];
|
|
rolePerms.value = rp.data?.permissions ?? [];
|
|
}
|
|
|
|
async function saveRolePerms() {
|
|
try {
|
|
const r = await api<{ message?: string }>("/v1/iam/roles/user/permissions", {
|
|
method: "PUT",
|
|
json: { permissions: rolePerms.value },
|
|
});
|
|
toast.add({ severity: "success", summary: r.message || "Permisos guardados", life: 2500 });
|
|
} catch (e) {
|
|
toast.add({ severity: "error", summary: displayErrorMessage((e as { data?: unknown }).data ?? e), life: 4000 });
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
desk.configure({ tab: "Usuarios IAM", ribbon: [] });
|
|
await load();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px 16px; }
|
|
.perm-item { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
|
</style>
|