clqms-be/app/Views/v2/organization.php
2025-12-22 16:54:19 +07:00

296 lines
13 KiB
PHP

<?= $this->extend('layouts/v2') ?>
<?= $this->section('content') ?>
<div x-data="organizationManager()">
<div class="flex items-center gap-4 mb-6">
<div>
<h2 class="text-2xl font-bold">Organization</h2>
<p class="text-base-content/60">Manage <span x-text="activeTab + 's'"></span></p>
</div>
</div>
<!-- Removed Tab List -->
<!-- The tab is now determined by the URL parameter passed from controller -->
<!-- Generic Data Table -->
<div class="card bg-base-100 shadow">
<div class="card-body">
<div class="flex justify-between items-center mb-4">
<h3 class="card-title capitalize" x-text="activeTab + 's'"></h3>
<button @click="openModal()" class="btn btn-primary btn-sm gap-2">
<i data-lucide="plus" class="w-4 h-4"></i>
Add <span class="capitalize" x-text="activeTab"></span>
</button>
</div>
<!-- Loading -->
<template x-if="isLoading">
<div class="flex justify-center p-8">
<span class="loading loading-spinner text-primary"></span>
</div>
</template>
<!-- Table -->
<div class="overflow-x-auto" x-show="!isLoading">
<table class="table table-zebra">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<!-- Dynamic Headers based on type -->
<template x-if="activeTab === 'account'"><th>Parent Account</th></template>
<template x-if="activeTab === 'site'"><th>Account ID</th></template>
<template x-if="activeTab === 'department'"><th>Site ID</th></template>
<template x-if="activeTab === 'workstation'"><th>Department ID</th></template>
<th>Actions</th>
</tr>
</thead>
<tbody>
<template x-for="row in data" :key="getRowId(row)">
<tr>
<td class="font-mono text-xs" x-text="getRowId(row)"></td>
<td class="font-bold" x-text="getRowName(row)"></td>
<!-- Account specific -->
<template x-if="activeTab === 'account'">
<td x-text="row.Parent || '-'"></td>
</template>
<!-- Site specific -->
<template x-if="activeTab === 'site'">
<td x-text="row.AccountID || '-'"></td>
</template>
<!-- Department specific -->
<template x-if="activeTab === 'department'">
<td x-text="row.SiteID || '-'"></td>
</template>
<!-- Workstation specific -->
<template x-if="activeTab === 'workstation'">
<td x-text="row.DepartmentID || '-'"></td>
</template>
<td>
<div class="flex gap-2">
<button @click="editRow(row)" class="btn btn-xs btn-ghost btn-square">
<i data-lucide="pencil" class="w-4 h-4"></i>
</button>
<button @click="deleteRow(row)" class="btn btn-xs btn-ghost btn-square text-error">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
</td>
</tr>
</template>
<template x-if="data.length === 0">
<tr>
<td colspan="4" class="text-center text-base-content/50 py-4">No records found</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
<!-- Generic Modal -->
<dialog id="orgModal" class="modal" :class="{ 'modal-open': isModalOpen }">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4" x-text="isEdit ? 'Edit ' + activeTab : 'New ' + activeTab"></h3>
<form @submit.prevent="save">
<div class="form-control w-full mb-4">
<label class="label"><span class="label-text">Name</span></label>
<input type="text" x-model="form.Name" class="input input-bordered w-full" required />
</div>
<!-- Account Fields -->
<template x-if="activeTab === 'account'">
<div class="form-control w-full mb-4">
<label class="label"><span class="label-text">Parent Account</span></label>
<input type="text" x-model="form.Parent" class="input input-bordered w-full" placeholder="Optional" />
</div>
</template>
<!-- Site Fields: Account ID Link -->
<template x-if="activeTab === 'site'">
<div class="form-control w-full mb-4">
<label class="label"><span class="label-text">Account ID</span></label>
<input type="number" x-model="form.AccountID" class="input input-bordered w-full" required />
</div>
</template>
<!-- Department Fields: Site ID Link -->
<template x-if="activeTab === 'department'">
<div class="form-control w-full mb-4">
<label class="label"><span class="label-text">Site ID</span></label>
<input type="number" x-model="form.SiteID" class="input input-bordered w-full" required />
</div>
</template>
<!-- Workstation Fields: Department ID Link -->
<template x-if="activeTab === 'workstation'">
<div class="form-control w-full mb-4">
<label class="label"><span class="label-text">Department ID</span></label>
<input type="number" x-model="form.DepartmentID" class="input input-bordered w-full" required />
</div>
</template>
<div class="modal-action">
<button type="button" class="btn" @click="closeModal">Cancel</button>
<button type="submit" class="btn btn-primary" :disabled="isSaving">
<span x-show="isSaving" class="loading loading-spinner loading-xs"></span>
Save
</button>
</div>
</form>
</div>
</dialog>
</div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script type="module">
import Alpine, { Utils } from '<?= base_url('/assets/js/app.js'); ?>';
document.addEventListener('alpine:init', () => {
Alpine.data('organizationManager', () => ({
// Initialize with the type passed from PHP view
activeTab: '<?= $type ?? 'account' ?>',
data: [],
isLoading: false,
isModalOpen: false,
isEdit: false,
isSaving: false,
form: {},
init() {
this.loadData();
},
// NOTE: setTab removed as we use routing now
async loadData() {
this.isLoading = true;
this.data = [];
try {
const response = await Utils.api(`<?= site_url('api/organization/') ?>${this.activeTab}`);
this.data = response.data || [];
// Re-render icons
setTimeout(() => window.lucide?.createIcons(), 50);
} catch (e) {
Alpine.store('toast').error(e.message);
} finally {
this.isLoading = false;
}
},
getRowId(row) {
if(this.activeTab === 'account') return row.AccountID;
if(this.activeTab === 'site') return row.SiteID;
if(this.activeTab === 'discipline') return row.DisciplineID;
if(this.activeTab === 'department') return row.DepartmentID;
if(this.activeTab === 'workstation') return row.WorkstationID;
return row.ID;
},
getRowName(row) {
if(this.activeTab === 'account') return row.AccountName;
if(this.activeTab === 'site') return row.SiteName;
if(this.activeTab === 'discipline') return row.DisciplineName;
if(this.activeTab === 'department') return row.DepartmentName;
if(this.activeTab === 'workstation') return row.WorkstationName;
return row.Name;
},
openModal() {
this.isEdit = false;
this.form = { Name: '' };
this.isModalOpen = true;
},
editRow(row) {
this.isEdit = true;
this.form = { ...row };
// Map specific name fields to generic 'Name' for the form input
if(this.activeTab === 'account') this.form.Name = row.AccountName;
if(this.activeTab === 'site') this.form.Name = row.SiteName;
if(this.activeTab === 'discipline') this.form.Name = row.DisciplineName;
if(this.activeTab === 'department') this.form.Name = row.DepartmentName;
if(this.activeTab === 'workstation') this.form.Name = row.WorkstationName;
this.isModalOpen = true;
},
closeModal() {
this.isModalOpen = false;
},
async save() {
this.isSaving = true;
try {
const payload = { ...this.form };
// Map generic Name back to specific field
if(this.activeTab === 'account') payload.AccountName = this.form.Name;
if(this.activeTab === 'site') payload.SiteName = this.form.Name;
if(this.activeTab === 'discipline') payload.DisciplineName = this.form.Name;
if(this.activeTab === 'department') payload.DepartmentName = this.form.Name;
if(this.activeTab === 'workstation') payload.WorkstationName = this.form.Name;
// ID for updates
if(this.isEdit) {
if(this.activeTab === 'account') payload.AccountID = this.form.AccountID;
if(this.activeTab === 'site') payload.SiteID = this.form.SiteID;
if(this.activeTab === 'discipline') payload.DisciplineID = this.form.DisciplineID;
if(this.activeTab === 'department') payload.DepartmentID = this.form.DepartmentID;
if(this.activeTab === 'workstation') payload.WorkstationID = this.form.WorkstationID;
}
const method = this.isEdit ? 'PATCH' : 'POST';
await Utils.api(`<?= site_url('api/organization/') ?>${this.activeTab}`, {
method,
body: JSON.stringify(payload)
});
Alpine.store('toast').success('Saved successfully');
this.closeModal();
this.loadData();
} catch (e) {
Alpine.store('toast').error(e.message);
} finally {
this.isSaving = false;
}
},
async deleteRow(row) {
if(!confirm('Are you sure you want to delete this item?')) return;
try {
const id = this.getRowId(row);
const idField = this.activeTab === 'account' ? 'AccountID' :
this.activeTab === 'site' ? 'SiteID' :
this.activeTab === 'discipline' ? 'DisciplineID' :
this.activeTab === 'department' ? 'DepartmentID' :
this.activeTab === 'workstation' ? 'WorkstationID' : 'ID';
await Utils.api(`<?= site_url('api/organization/') ?>${this.activeTab}`, {
method: 'DELETE',
body: JSON.stringify({ [idField]: id })
});
Alpine.store('toast').success('Deleted successfully');
this.loadData();
} catch (e) {
Alpine.store('toast').error(e.message);
}
}
}));
});
Alpine.start();
</script>
<?= $this->endSection() ?>