Files
web-probindo/resources/js/pages/admin/testimonials/index.tsx
T
afriadirahman 8c6dad2a31
tests / ci (push) Canceled after 0s
Admin Panel
2026-09-29 14:57:37 +07:00

245 lines
10 KiB
TypeScript

import { Head } from '@inertiajs/react';
import { Pencil, Plus, Quote, Trash2 } from 'lucide-react';
import type { ReactNode } from 'react';
import {
ConfirmDelete,
FormDialog,
FormField,
PageHeader,
Textarea,
} from '@/components/admin/form-controls';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { destroy, index, store, update } from '@/routes/admin/testimonials';
import type { Testimonial } from '@/types';
export default function TestimonialsIndex({
testimonials,
}: {
testimonials: Testimonial[];
}) {
return (
<>
<Head title="Testimoni" />
<div className="flex flex-col gap-6 p-4 md:p-6">
<PageHeader
title="Testimoni"
description="Bagian testimoni di halaman Klien hanya muncul jika ada testimoni yang dipublikasikan. Gunakan hanya testimoni asli yang sudah disetujui klien."
action={
<TestimonialDialog
nextOrder={testimonials.length + 1}
trigger={
<Button>
<Plus /> Tambah Testimoni
</Button>
}
/>
}
/>
{testimonials.length === 0 ? (
<div className="text-muted-foreground rounded-xl border border-dashed p-12 text-center text-sm">
Belum ada testimoni. Bagian testimoni sedang
disembunyikan di website.
</div>
) : (
<div className="grid gap-4 md:grid-cols-2">
{testimonials.map((testimonial) => (
<div
key={testimonial.id}
className="bg-card flex flex-col gap-3 rounded-xl border p-5"
>
<div className="flex items-start justify-between gap-2">
<Quote className="text-primary size-5" />
<div className="flex items-center gap-1">
<Badge
variant={
testimonial.is_published
? 'default'
: 'secondary'
}
>
{testimonial.is_published
? 'Tayang'
: 'Draf'}
</Badge>
<TestimonialDialog
testimonial={testimonial}
trigger={
<Button
variant="ghost"
size="icon"
aria-label="Ubah"
>
<Pencil />
</Button>
}
/>
<ConfirmDelete
title="Hapus testimoni ini?"
description={`Testimoni dari ${testimonial.organization_id} akan dihapus permanen.`}
form={destroy.form(testimonial.id)}
trigger={
<Button
variant="ghost"
size="icon"
aria-label="Hapus"
className="text-destructive"
>
<Trash2 />
</Button>
}
/>
</div>
</div>
<p className="text-sm italic">
“{testimonial.quote_id}”
</p>
<span className="text-muted-foreground text-xs">
{testimonial.role_id} ·{' '}
{testimonial.organization_id}
</span>
</div>
))}
</div>
)}
</div>
</>
);
}
function TestimonialDialog({
testimonial,
nextOrder = 1,
trigger,
}: {
testimonial?: Testimonial;
nextOrder?: number;
trigger: ReactNode;
}) {
const id = (field: string) =>
`testimonial-${testimonial?.id ?? 'new'}-${field}`;
return (
<FormDialog
trigger={trigger}
title={testimonial ? 'Ubah Testimoni' : 'Tambah Testimoni'}
description="Isi versi Bahasa Indonesia dan English; website menampilkan sesuai bahasa pengunjung."
form={testimonial ? update.form(testimonial.id) : store.form()}
className="sm:max-w-2xl"
>
{(errors) => (
<>
<div className="grid gap-4 sm:grid-cols-2">
<FormField
id={id('quote_id')}
label="Kutipan (ID)"
error={errors.quote_id}
>
<Textarea
id={id('quote_id')}
name="quote_id"
defaultValue={testimonial?.quote_id}
required
/>
</FormField>
<FormField
id={id('quote_en')}
label="Quote (EN)"
error={errors.quote_en}
>
<Textarea
id={id('quote_en')}
name="quote_en"
defaultValue={testimonial?.quote_en}
required
/>
</FormField>
<FormField
id={id('role_id')}
label="Jabatan (ID)"
error={errors.role_id}
>
<Input
id={id('role_id')}
name="role_id"
defaultValue={testimonial?.role_id}
required
/>
</FormField>
<FormField
id={id('role_en')}
label="Role (EN)"
error={errors.role_en}
>
<Input
id={id('role_en')}
name="role_en"
defaultValue={testimonial?.role_en}
required
/>
</FormField>
<FormField
id={id('organization_id')}
label="Organisasi (ID)"
error={errors.organization_id}
>
<Input
id={id('organization_id')}
name="organization_id"
defaultValue={testimonial?.organization_id}
required
/>
</FormField>
<FormField
id={id('organization_en')}
label="Organization (EN)"
error={errors.organization_en}
>
<Input
id={id('organization_en')}
name="organization_en"
defaultValue={testimonial?.organization_en}
required
/>
</FormField>
<FormField
id={id('sort_order')}
label="Nomor urut"
error={errors.sort_order}
>
<Input
id={id('sort_order')}
name="sort_order"
type="number"
min={0}
defaultValue={
testimonial?.sort_order ?? nextOrder
}
required
/>
</FormField>
<label className="flex items-center gap-2 self-end pb-2 text-sm font-medium">
<input
type="checkbox"
name="is_published"
value="1"
defaultChecked={
testimonial?.is_published ?? true
}
className="accent-primary size-4"
/>
Tampilkan di website
</label>
</div>
</>
)}
</FormDialog>
);
}
TestimonialsIndex.layout = {
breadcrumbs: [{ title: 'Testimoni', href: index() }],
};