922 lines
44 KiB
TypeScript
922 lines
44 KiB
TypeScript
import { Form, Head } from '@inertiajs/react';
|
|
import { Plus, Trash2 } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { toast } from 'sonner';
|
|
import { FileUpload } from '@/components/inputs';
|
|
import { InputError } from '@/components/ui';
|
|
import { PhoneNumberInput } from '@/components/inputs';
|
|
import { RupiahInput } from '@/components/inputs';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { cn } from '@/lib/utils';
|
|
import {
|
|
updateHomepage,
|
|
updateHr,
|
|
updateMarketplace,
|
|
updateSocialMedia,
|
|
updateSystem,
|
|
} from '@/routes/admin/settings';
|
|
|
|
type MarketplaceFeeRule = {
|
|
base: string;
|
|
type: string;
|
|
value: number;
|
|
};
|
|
|
|
type Props = {
|
|
system: {
|
|
app_name: string;
|
|
about_app: string;
|
|
address: string;
|
|
email: string;
|
|
phone: string;
|
|
};
|
|
homepage: {
|
|
hero_image_url: string;
|
|
hero_image_key: string;
|
|
about_image_url: string;
|
|
about_image_key: string;
|
|
gallery_images: string[];
|
|
gallery_image_keys: string[];
|
|
};
|
|
socialMedia: {
|
|
instagram_url: string;
|
|
facebook_url: string;
|
|
tiktok_url: string;
|
|
};
|
|
marketplace: {
|
|
tiktok_shop: Record<string, MarketplaceFeeRule>;
|
|
shopee: Record<string, MarketplaceFeeRule>;
|
|
};
|
|
hr: {
|
|
scheduled_check_in_time: string;
|
|
scheduled_check_out_time: string;
|
|
late_penalty_amount: number;
|
|
absent_penalty_amount: number;
|
|
};
|
|
};
|
|
|
|
type HomepageSettingsTabProps = {
|
|
homepage: Props['homepage'];
|
|
};
|
|
|
|
function HomepageSettingsTab({ homepage }: HomepageSettingsTabProps) {
|
|
const [heroKey, setHeroKey] = useState<string | null>(
|
|
homepage.hero_image_key ?? null,
|
|
);
|
|
const [heroUploading, setHeroUploading] = useState(false);
|
|
const [aboutKey, setAboutKey] = useState<string | null>(
|
|
homepage.about_image_key ?? null,
|
|
);
|
|
const [aboutUploading, setAboutUploading] = useState(false);
|
|
const [galleryKeys, setGalleryKeys] = useState<string[]>(
|
|
homepage.gallery_image_keys ?? [],
|
|
);
|
|
const [galleryUploading, setGalleryUploading] = useState(false);
|
|
|
|
function addGalleryImage() {
|
|
setGalleryKeys((prev) => [...prev, '']);
|
|
}
|
|
|
|
function removeGalleryImage(index: number) {
|
|
setGalleryKeys((prev) => prev.filter((_, i) => i !== index));
|
|
}
|
|
|
|
function updateGalleryKey(index: number, key: string | null) {
|
|
setGalleryKeys((prev) =>
|
|
prev.map((k, i) => (i === index ? (key ?? '') : k)),
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Form
|
|
action={updateHomepage()}
|
|
options={{ preserveScroll: true }}
|
|
onError={() => {
|
|
toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.');
|
|
}}
|
|
>
|
|
{({ processing, errors }) => (
|
|
<div className="grid gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Homepage</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid grid-cols-1 gap-4">
|
|
<input
|
|
type="hidden"
|
|
name="hero_image_key"
|
|
value={heroKey ?? ''}
|
|
/>
|
|
<input
|
|
type="hidden"
|
|
name="about_image_key"
|
|
value={aboutKey ?? ''}
|
|
/>
|
|
{galleryKeys.map((key, index) => (
|
|
<input
|
|
key={index}
|
|
type="hidden"
|
|
name={`gallery_image_keys[${index}]`}
|
|
value={key}
|
|
/>
|
|
))}
|
|
|
|
<div className="grid gap-2">
|
|
<Label>Foto Hero</Label>
|
|
<FileUpload
|
|
value={heroKey}
|
|
onChange={setHeroKey}
|
|
folder="homepage/hero"
|
|
existingUrl={homepage.hero_image_url}
|
|
onUploadingChange={setHeroUploading}
|
|
/>
|
|
<InputError message={errors.hero_image_key} />
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
<Label>Foto Tentang Kami</Label>
|
|
<FileUpload
|
|
value={aboutKey}
|
|
onChange={setAboutKey}
|
|
folder="homepage/about"
|
|
existingUrl={homepage.about_image_url}
|
|
onUploadingChange={setAboutUploading}
|
|
/>
|
|
<InputError message={errors.about_image_key} />
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label>Galeri Foto</Label>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={addGalleryImage}
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
Tambah
|
|
</Button>
|
|
</div>
|
|
<div className="grid gap-3">
|
|
{galleryKeys.map((key, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex items-start gap-2"
|
|
>
|
|
<div className="flex-1">
|
|
<FileUpload
|
|
value={key || null}
|
|
onChange={(k) =>
|
|
updateGalleryKey(
|
|
index,
|
|
k,
|
|
)
|
|
}
|
|
folder="homepage/gallery"
|
|
existingUrl={
|
|
homepage.gallery_images[
|
|
index
|
|
] ?? null
|
|
}
|
|
onUploadingChange={
|
|
setGalleryUploading
|
|
}
|
|
/>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="mt-1 text-destructive hover:text-destructive"
|
|
onClick={() =>
|
|
removeGalleryImage(index)
|
|
}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
{galleryKeys.length === 0 && (
|
|
<p className="text-sm text-muted-foreground">
|
|
Belum ada gambar. Klik "Tambah"
|
|
untuk menambahkan gambar galeri.
|
|
</p>
|
|
)}
|
|
</div>
|
|
<InputError
|
|
message={errors.gallery_image_keys}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
disabled={
|
|
processing ||
|
|
heroUploading ||
|
|
aboutUploading ||
|
|
galleryUploading
|
|
}
|
|
>
|
|
{processing ? 'Menyimpan...' : 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Form>
|
|
);
|
|
}
|
|
|
|
const sidebarTabs = [
|
|
{ key: 'sistem', label: 'Sistem' },
|
|
{ key: 'homepage', label: 'Homepage' },
|
|
{ key: 'media-sosial', label: 'Media Sosial' },
|
|
{ key: 'marketplace', label: 'Marketplace' },
|
|
{ key: 'hr', label: 'HR' },
|
|
] as const;
|
|
|
|
type TabKey = (typeof sidebarTabs)[number]['key'];
|
|
|
|
function MarketplaceVariableInput({
|
|
label,
|
|
prefix,
|
|
data,
|
|
}: {
|
|
label: string;
|
|
prefix: string;
|
|
data: MarketplaceFeeRule;
|
|
}) {
|
|
const [type, setType] = useState(data.type);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 rounded-md border p-3 md:flex-row md:items-start md:gap-4">
|
|
<span className="min-w-[160px] text-sm font-medium md:pt-2">
|
|
{label}
|
|
</span>
|
|
<div className="grid flex-1 grid-cols-1 gap-3 md:grid-cols-3">
|
|
<div className="grid gap-2">
|
|
<span className="text-xs font-medium text-muted-foreground md:hidden">
|
|
Dasar
|
|
</span>
|
|
<RadioGroup
|
|
name={`${prefix}[base]`}
|
|
defaultValue={data.base}
|
|
className="flex gap-4"
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem
|
|
value="per_transaksi"
|
|
id={`${prefix}-base-per_transaksi`}
|
|
/>
|
|
<Label
|
|
htmlFor={`${prefix}-base-per_transaksi`}
|
|
className="text-xs font-normal"
|
|
>
|
|
Per Transaksi
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem
|
|
value="per_produk"
|
|
id={`${prefix}-base-per_produk`}
|
|
/>
|
|
<Label
|
|
htmlFor={`${prefix}-base-per_produk`}
|
|
className="text-xs font-normal"
|
|
>
|
|
Per Produk
|
|
</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<span className="text-xs font-medium text-muted-foreground md:hidden">
|
|
Tipe
|
|
</span>
|
|
<RadioGroup
|
|
name={`${prefix}[type]`}
|
|
defaultValue={data.type}
|
|
onValueChange={setType}
|
|
className="flex gap-4"
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem
|
|
value="flat"
|
|
id={`${prefix}-type-flat`}
|
|
/>
|
|
<Label
|
|
htmlFor={`${prefix}-type-flat`}
|
|
className="text-xs font-normal"
|
|
>
|
|
Flat
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem
|
|
value="persentase"
|
|
id={`${prefix}-type-persentase`}
|
|
/>
|
|
<Label
|
|
htmlFor={`${prefix}-type-persentase`}
|
|
className="text-xs font-normal"
|
|
>
|
|
Persentase
|
|
</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<span className="text-xs font-medium text-muted-foreground md:hidden">
|
|
Nilai
|
|
</span>
|
|
{type === 'flat' ? (
|
|
<RupiahInput
|
|
name={`${prefix}[value]`}
|
|
defaultValue={data.value}
|
|
/>
|
|
) : (
|
|
<Input
|
|
name={`${prefix}[value]`}
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
defaultValue={data.value}
|
|
placeholder="0"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MarketplaceColumnHeader() {
|
|
return (
|
|
<div className="hidden gap-3 px-3 pb-1 text-xs font-medium text-muted-foreground md:grid md:grid-cols-[160px_1fr] md:gap-4">
|
|
<span>Biaya</span>
|
|
<div className="grid flex-1 grid-cols-3 gap-3">
|
|
<span>Dasar</span>
|
|
<span>Tipe</span>
|
|
<span>Nilai</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function MarketplaceCard({
|
|
title,
|
|
variables,
|
|
}: {
|
|
title: string;
|
|
variables: { label: string; prefix: string; data: MarketplaceFeeRule }[];
|
|
}) {
|
|
return (
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-base">{title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid grid-cols-1 gap-2">
|
|
<MarketplaceColumnHeader />
|
|
{variables.map((v) => (
|
|
<MarketplaceVariableInput key={v.prefix} {...v} />
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
export default function AdminSettings({
|
|
system,
|
|
homepage,
|
|
socialMedia,
|
|
marketplace,
|
|
hr,
|
|
}: Props) {
|
|
const [activeTab, setActiveTab] = useState<TabKey>('sistem');
|
|
const [marketplaceTab, setMarketplaceTab] = useState<
|
|
'tiktok-shop' | 'shopee'
|
|
>('tiktok-shop');
|
|
|
|
const tiktokShopVariables = [
|
|
{
|
|
label: 'Komisi Platform',
|
|
prefix: 'tiktok_shop_platform_commission',
|
|
data: marketplace.tiktok_shop.platform_commission,
|
|
},
|
|
{
|
|
label: 'Layanan Logistik',
|
|
prefix: 'tiktok_shop_logistics_service_fee',
|
|
data: marketplace.tiktok_shop.logistics_service_fee,
|
|
},
|
|
{
|
|
label: 'Komisi Dinamis',
|
|
prefix: 'tiktok_shop_dynamic_commission',
|
|
data: marketplace.tiktok_shop.dynamic_commission,
|
|
},
|
|
{
|
|
label: 'Pemrosesan Pesanan',
|
|
prefix: 'tiktok_shop_order_processing_fee',
|
|
data: marketplace.tiktok_shop.order_processing_fee,
|
|
},
|
|
{
|
|
label: 'Affiliate',
|
|
prefix: 'tiktok_shop_affiliate',
|
|
data: marketplace.tiktok_shop.affiliate,
|
|
},
|
|
{
|
|
label: 'Layanan PO',
|
|
prefix: 'tiktok_shop_pre_order_service_fee',
|
|
data: marketplace.tiktok_shop.pre_order_service_fee,
|
|
},
|
|
];
|
|
|
|
const shopeeVariables = [
|
|
{
|
|
label: 'Biaya Administrasi',
|
|
prefix: 'shopee_admin_fee',
|
|
data: marketplace.shopee.admin_fee,
|
|
},
|
|
{
|
|
label: 'Biaya Program',
|
|
prefix: 'shopee_program_fee',
|
|
data: marketplace.shopee.program_fee,
|
|
},
|
|
{
|
|
label: 'Hemat Biaya Kirim',
|
|
prefix: 'shopee_shipping_savings',
|
|
data: marketplace.shopee.shipping_savings,
|
|
},
|
|
{
|
|
label: 'Premi',
|
|
prefix: 'shopee_premium',
|
|
data: marketplace.shopee.premium,
|
|
},
|
|
{
|
|
label: 'Biaya Layanan',
|
|
prefix: 'shopee_service_fee',
|
|
data: marketplace.shopee.service_fee,
|
|
},
|
|
{
|
|
label: 'Biaya Proses Pesanan',
|
|
prefix: 'shopee_order_processing_fee',
|
|
data: marketplace.shopee.order_processing_fee,
|
|
},
|
|
{
|
|
label: 'Biaya Komisi AMS',
|
|
prefix: 'shopee_ams_commission_fee',
|
|
data: marketplace.shopee.ams_commission_fee,
|
|
},
|
|
{
|
|
label: 'PO',
|
|
prefix: 'shopee_pre_order',
|
|
data: marketplace.shopee.pre_order,
|
|
},
|
|
{
|
|
label: 'Live Extra',
|
|
prefix: 'shopee_live_extra',
|
|
data: marketplace.shopee.live_extra,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<Head title="Pengaturan" />
|
|
|
|
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-2xl font-semibold tracking-tight">
|
|
Pengaturan
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col lg:flex-row lg:space-x-12">
|
|
<aside className="w-full max-w-xl lg:w-48">
|
|
<nav
|
|
className="flex flex-col space-y-1 space-x-0"
|
|
aria-label="Admin Settings"
|
|
>
|
|
{sidebarTabs.map((tab) => (
|
|
<Button
|
|
key={tab.key}
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => setActiveTab(tab.key)}
|
|
className={cn('w-full justify-start', {
|
|
'bg-muted': activeTab === tab.key,
|
|
})}
|
|
>
|
|
{tab.label}
|
|
</Button>
|
|
))}
|
|
</nav>
|
|
</aside>
|
|
|
|
<Separator className="my-6 lg:hidden" />
|
|
|
|
<section className="w-full min-w-0 flex-1 space-y-6">
|
|
{activeTab === 'sistem' && (
|
|
<Form
|
|
action={updateSystem()}
|
|
options={{ preserveScroll: true }}
|
|
onError={() => {
|
|
toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.');
|
|
}}
|
|
>
|
|
{({ processing, errors }) => (
|
|
<div className="grid gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Sistem</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<div className="grid gap-2 md:col-span-2">
|
|
<Label htmlFor="app_name">
|
|
Nama Aplikasi{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<Input
|
|
id="app_name"
|
|
name="app_name"
|
|
placeholder="Masukkan nama aplikasi"
|
|
defaultValue={
|
|
system.app_name
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.app_name
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="email">
|
|
Email
|
|
</Label>
|
|
<Input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
placeholder="Masukkan email"
|
|
defaultValue={
|
|
system.email
|
|
}
|
|
/>
|
|
<InputError
|
|
message={errors.email}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="phone">
|
|
No. Telepon
|
|
</Label>
|
|
<PhoneNumberInput
|
|
name="phone"
|
|
defaultValue={
|
|
system.phone
|
|
}
|
|
/>
|
|
<InputError
|
|
message={errors.phone}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2 md:col-span-2">
|
|
<Label htmlFor="address">
|
|
Alamat
|
|
</Label>
|
|
<Textarea
|
|
id="address"
|
|
name="address"
|
|
placeholder="Masukkan alamat"
|
|
rows={3}
|
|
defaultValue={
|
|
system.address
|
|
}
|
|
/>
|
|
<InputError
|
|
message={errors.address}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2 md:col-span-2">
|
|
<Label htmlFor="about_app">
|
|
Tentang Aplikasi
|
|
</Label>
|
|
<Textarea
|
|
id="about_app"
|
|
name="about_app"
|
|
placeholder="Masukkan deskripsi aplikasi"
|
|
rows={4}
|
|
defaultValue={
|
|
system.about_app
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.about_app
|
|
}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
disabled={processing}
|
|
>
|
|
{processing
|
|
? 'Menyimpan...'
|
|
: 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Form>
|
|
)}
|
|
|
|
{activeTab === 'homepage' && (
|
|
<HomepageSettingsTab homepage={homepage} />
|
|
)}
|
|
|
|
{activeTab === 'media-sosial' && (
|
|
<Form
|
|
action={updateSocialMedia()}
|
|
options={{ preserveScroll: true }}
|
|
onError={() => {
|
|
toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.');
|
|
}}
|
|
>
|
|
{({ processing, errors }) => (
|
|
<div className="grid gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>
|
|
Media Sosial
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="instagram_url">
|
|
Instagram
|
|
</Label>
|
|
<Input
|
|
id="instagram_url"
|
|
name="instagram_url"
|
|
placeholder="https://instagram.com/..."
|
|
defaultValue={
|
|
socialMedia.instagram_url ??
|
|
''
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.instagram_url
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="facebook_url">
|
|
Facebook
|
|
</Label>
|
|
<Input
|
|
id="facebook_url"
|
|
name="facebook_url"
|
|
placeholder="https://facebook.com/..."
|
|
defaultValue={
|
|
socialMedia.facebook_url ??
|
|
''
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.facebook_url
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="tiktok_url">
|
|
TikTok
|
|
</Label>
|
|
<Input
|
|
id="tiktok_url"
|
|
name="tiktok_url"
|
|
placeholder="https://tiktok.com/..."
|
|
defaultValue={
|
|
socialMedia.tiktok_url ??
|
|
''
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.tiktok_url
|
|
}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
disabled={processing}
|
|
>
|
|
{processing
|
|
? 'Menyimpan...'
|
|
: 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Form>
|
|
)}
|
|
|
|
{activeTab === 'marketplace' && (
|
|
<Form
|
|
action={updateMarketplace()}
|
|
options={{ preserveScroll: true }}
|
|
onError={() => {
|
|
toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.');
|
|
}}
|
|
>
|
|
{({ processing }) => (
|
|
<div className="grid gap-6">
|
|
<Tabs
|
|
value={marketplaceTab}
|
|
onValueChange={(v) =>
|
|
setMarketplaceTab(
|
|
v as
|
|
| 'tiktok-shop'
|
|
| 'shopee',
|
|
)
|
|
}
|
|
>
|
|
<TabsList className="mb-3">
|
|
<TabsTrigger value="tiktok-shop">
|
|
TikTok Shop
|
|
</TabsTrigger>
|
|
<TabsTrigger value="shopee">
|
|
Shopee
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
</Tabs>
|
|
<div
|
|
hidden={
|
|
marketplaceTab !== 'tiktok-shop'
|
|
}
|
|
>
|
|
<MarketplaceCard
|
|
title="TikTok Shop"
|
|
variables={tiktokShopVariables}
|
|
/>
|
|
</div>
|
|
<div
|
|
hidden={marketplaceTab !== 'shopee'}
|
|
>
|
|
<MarketplaceCard
|
|
title="Shopee"
|
|
variables={shopeeVariables}
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
disabled={processing}
|
|
>
|
|
{processing
|
|
? 'Menyimpan...'
|
|
: 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Form>
|
|
)}
|
|
|
|
{activeTab === 'hr' && (
|
|
<Form
|
|
action={updateHr()}
|
|
options={{ preserveScroll: true }}
|
|
onError={() => {
|
|
toast.error('Ada data yang belum sesuai, silakan periksa kembali input Anda.');
|
|
}}
|
|
>
|
|
{({ processing, errors }) => (
|
|
<div className="grid gap-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>HR</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="scheduled_check_in_time">
|
|
Jam Masuk Kerja{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<Input
|
|
id="scheduled_check_in_time"
|
|
name="scheduled_check_in_time"
|
|
type="time"
|
|
defaultValue={
|
|
hr.scheduled_check_in_time
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.scheduled_check_in_time
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="scheduled_check_out_time">
|
|
Jam Pulang Kerja{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<Input
|
|
id="scheduled_check_out_time"
|
|
name="scheduled_check_out_time"
|
|
type="time"
|
|
defaultValue={
|
|
hr.scheduled_check_out_time
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.scheduled_check_out_time
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
Denda Keterlambatan{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<RupiahInput
|
|
name="late_penalty_amount"
|
|
defaultValue={
|
|
hr.late_penalty_amount
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.late_penalty_amount
|
|
}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label>
|
|
Denda Bolos{' '}
|
|
<span className="text-destructive">
|
|
*
|
|
</span>
|
|
</Label>
|
|
<RupiahInput
|
|
name="absent_penalty_amount"
|
|
defaultValue={
|
|
hr.absent_penalty_amount
|
|
}
|
|
/>
|
|
<InputError
|
|
message={
|
|
errors.absent_penalty_amount
|
|
}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<div className="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
disabled={processing}
|
|
>
|
|
{processing
|
|
? 'Menyimpan...'
|
|
: 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Form>
|
|
)}
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|