feat: add leadership history dialog and integrate into DepartmentIndex
This commit is contained in:
parent
f00fab9520
commit
2b458e26fa
@ -14,7 +14,11 @@ class DepartmentService
|
||||
public function paginated(int $perPage = 25, string $search = ''): LengthAwarePaginator
|
||||
{
|
||||
return Department::query()
|
||||
->with(['currentLeader.lecturer.user.profile'])
|
||||
->with([
|
||||
'currentLeader.lecturer.user.profile',
|
||||
'leaderships' => fn ($q) => $q->orderByRaw('ended_at IS NULL DESC')->orderByDesc('started_at'),
|
||||
'leaderships.lecturer.user.profile',
|
||||
])
|
||||
->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%")->orWhere('code', 'like', "%{$search}%"))
|
||||
->latest()
|
||||
->paginate($perPage, ['id', 'code', 'name', 'degree_level']);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { Pencil, Trash2 } from 'lucide-react';
|
||||
import { History, Pencil, Trash2 } from 'lucide-react';
|
||||
import { RowActions } from '@/components/row-actions';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import type { Department } from '@/types/department';
|
||||
@ -9,6 +9,7 @@ export type { Department } from '@/types/department';
|
||||
type CreateColumnsParams = {
|
||||
handleEdit: (department: Department) => void;
|
||||
handleDeleteClick: (department: Department) => void;
|
||||
handleViewHistory: (department: Department) => void;
|
||||
canUpdate: boolean;
|
||||
canDelete: boolean;
|
||||
};
|
||||
@ -16,7 +17,13 @@ type CreateColumnsParams = {
|
||||
export function createDepartmentColumns(
|
||||
params: CreateColumnsParams,
|
||||
): ColumnDef<Department>[] {
|
||||
const { handleEdit, handleDeleteClick, canUpdate, canDelete } = params;
|
||||
const {
|
||||
handleEdit,
|
||||
handleDeleteClick,
|
||||
handleViewHistory,
|
||||
canUpdate,
|
||||
canDelete,
|
||||
} = params;
|
||||
|
||||
const columns: ColumnDef<Department>[] = [
|
||||
{
|
||||
@ -72,7 +79,7 @@ export function createDepartmentColumns(
|
||||
},
|
||||
];
|
||||
|
||||
if (canUpdate || canDelete) {
|
||||
{
|
||||
columns.push({
|
||||
id: 'actions',
|
||||
header: () => <span className="block text-center">Aksi</span>,
|
||||
@ -86,6 +93,11 @@ export function createDepartmentColumns(
|
||||
return (
|
||||
<RowActions
|
||||
actions={[
|
||||
{
|
||||
label: 'Riwayat Kaprodi',
|
||||
icon: <History className="h-4 w-4" />,
|
||||
onClick: () => handleViewHistory(department),
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
icon: <Pencil className="h-4 w-4" />,
|
||||
|
||||
@ -9,6 +9,7 @@ import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog';
|
||||
import { FormDialog } from '@/components/form-dialog';
|
||||
import InputError from '@/components/input-error';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Combobox,
|
||||
@ -18,6 +19,12 @@ import {
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
} from '@/components/ui/combobox';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
@ -63,6 +70,8 @@ export default function DepartmentIndex({
|
||||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<Department | null>(null);
|
||||
const [deleting, setDeleting] = useState<Department | null>(null);
|
||||
const [historyDepartment, setHistoryDepartment] =
|
||||
useState<Department | null>(null);
|
||||
const { hasPermission } = usePermissions();
|
||||
const canCreate = hasPermission('create-departments');
|
||||
const canUpdate = hasPermission('update-departments');
|
||||
@ -98,6 +107,7 @@ export default function DepartmentIndex({
|
||||
const columns = createDepartmentColumns({
|
||||
handleEdit: (department) => setEditing(department),
|
||||
handleDeleteClick: (department) => setDeleting(department),
|
||||
handleViewHistory: (department) => setHistoryDepartment(department),
|
||||
canUpdate,
|
||||
canDelete,
|
||||
});
|
||||
@ -188,11 +198,79 @@ export default function DepartmentIndex({
|
||||
}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
|
||||
<LeadershipHistoryDialog
|
||||
department={historyDepartment}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setHistoryDepartment(null);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LeadershipHistoryDialog({
|
||||
department,
|
||||
onOpenChange,
|
||||
}: {
|
||||
department: Department | null;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}) {
|
||||
const entries = department?.leaderships ?? [];
|
||||
|
||||
return (
|
||||
<Dialog open={department !== null} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-h-[85vh] overflow-hidden sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Riwayat Kaprodi
|
||||
{department ? ` — ${department.name}` : ''}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="max-h-[60vh] space-y-3 overflow-y-auto">
|
||||
{entries.length === 0 && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Belum ada riwayat kaprodi untuk jurusan ini.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{entries.map((entry) => (
|
||||
<div
|
||||
key={entry.id}
|
||||
className="flex items-start justify-between gap-3 rounded-md border p-3"
|
||||
>
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
{entry.lecturer?.user?.profile
|
||||
?.full_name ?? 'N/A'}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{format(
|
||||
new Date(entry.started_at),
|
||||
'd MMM yyyy',
|
||||
)}{' '}
|
||||
-{' '}
|
||||
{entry.ended_at
|
||||
? format(
|
||||
new Date(entry.ended_at),
|
||||
'd MMM yyyy',
|
||||
)
|
||||
: 'Sekarang'}
|
||||
</p>
|
||||
</div>
|
||||
{!entry.ended_at && <Badge>Sekarang</Badge>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function KaprodiFields({
|
||||
errors,
|
||||
lecturers,
|
||||
|
||||
@ -23,6 +23,7 @@ export type Department = {
|
||||
name: string;
|
||||
degree_level: DegreeLevel | null;
|
||||
current_leader: DepartmentLeadership | null;
|
||||
leaderships: DepartmentLeadership[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user