import { Head, Link, router } from '@inertiajs/react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import AppLayout from '@/layouts/app-layout';
import { Printer, Building2, CalendarDays, Hash, Pencil, CheckCircle2, Clock } from 'lucide-react';
interface DetailItem {
name: string;
type: 'bonus' | 'deduction';
amount: number;
}
interface PayrollData {
id: number;
period: string;
basic_salary: number;
details: DetailItem[];
net_salary: number;
status: 'pending' | 'paid';
created_at: string;
employee: {
name: string;
nip: string;
position: string;
department: string;
};
}
interface PageProps {
payroll: PayrollData;
}
function formatRupiah(value: number): string {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0,
}).format(value);
}
function formatPeriod(period: string): string {
const [year, month] = period.split('-');
const date = new Date(Number(year), Number(month) - 1, 1);
return date.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' });
}
function InfoCell({ label, value }: { label: string; value: string }) {
return (
);
}
function LineItem({
label,
value,
type = 'neutral',
}: {
label: string;
value: string;
type?: 'neutral' | 'bonus' | 'deduction';
}) {
const valueClass =
type === 'bonus'
? 'text-emerald-600 font-medium'
: type === 'deduction'
? 'text-red-500 font-medium'
: 'font-medium';
const prefix = type === 'bonus' ? '+ ' : type === 'deduction' ? '− ' : '';
return (
{label}
{prefix}{value}
);
}
function StatusBadge({ status }: { status: 'pending' | 'paid' }) {
return status === 'paid' ? (
Telah Dikirim
) : (
Menunggu
);
}
export default function Show({ payroll }: PageProps) {
const bonuses = payroll.details.filter((d) => d.type === 'bonus');
const deductions = payroll.details.filter((d) => d.type === 'deduction');
const totalBonus = bonuses.reduce((s, d) => s + d.amount, 0);
const totalDeduction = deductions.reduce((s, d) => s + d.amount, 0);
const [toggling, setToggling] = useState(false);
const toggleStatus = () => {
const newStatus = payroll.status === 'paid' ? 'pending' : 'paid';
setToggling(true);
router.patch(
`/admin/payrolls/${payroll.id}/status`,
{ status: newStatus },
{ onFinish: () => setToggling(false) },
);
};
return (
{/* Toolbar — tersembunyi saat print */}
{payroll.status !== 'paid' && (
<>
>
)}
{/* ── Slip Card ── */}
{/* ── HEADER ── */}
{/* Nama Perusahaan */}
PT. HRIS Nusantara
Slip Gaji Karyawan
{/* Meta slip */}
{String(payroll.id).padStart(5, '0')}
{payroll.created_at}
{/* ── INFO KARYAWAN ── */}
{/* ── PERIODE ── */}
Periode Penggajian
{formatPeriod(payroll.period)}
{/* ── RINCIAN GAJI ── */}
{/* Gaji Pokok */}
Komponen Gaji
{/* Bonus */}
{bonuses.length > 0 && (
<>
Tambahan
{bonuses.map((item, i) => (
))}
Subtotal tambahan
+ {formatRupiah(totalBonus)}
>
)}
{/* Potongan */}
{deductions.length > 0 && (
<>
Potongan
{deductions.map((item, i) => (
))}
Subtotal potongan
− {formatRupiah(totalDeduction)}
>
)}
{/* ── TOTAL GAJI BERSIH ── */}
Total Gaji Bersih
Gaji Pokok
{bonuses.length > 0 && ' + Tambahan'}
{deductions.length > 0 && ' − Potongan'}
{formatRupiah(payroll.net_salary)}
{/* ── FOOTER ── */}
Dokumen ini diterbitkan secara otomatis oleh sistem HRIS dan sah tanpa tanda tangan basah.
);
}