store/resources/js/pages/admin/account/Appearance.vue

122 lines
4.6 KiB
Vue

<script setup lang="ts">
import { Head, useForm } from '@inertiajs/vue3';
import { Monitor, Moon, Save, Sun } from '@lucide/vue';
import { toast } from 'vue-sonner';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import {
Field,
FieldError,
FieldGroup,
FieldLabel,
FieldSet,
} from '@/components/ui/field';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { applyAppearance } from '@/composables/useAppearance';
import AccountLayout from '@/layouts/AccountLayout.vue';
import { cn } from '@/lib/utils';
import type { AppearanceFormData, AppearanceMode } from '@/types/account';
const props = defineProps<{
appearance: AppearanceMode;
}>();
const form = useForm<AppearanceFormData>({
appearance: props.appearance,
});
const options: Array<{
value: AppearanceMode;
label: string;
description: string;
icon: typeof Sun;
}> = [
{
value: 'light',
label: 'Terang',
description: 'Tampilan terang untuk lingkungan yang cukup pencahayaan.',
icon: Sun,
},
{
value: 'dark',
label: 'Gelap',
description: 'Tampilan gelap yang nyaman di malam hari.',
icon: Moon,
},
{
value: 'system',
label: 'Sistem',
description: 'Mengikuti pengaturan tampilan perangkat Anda.',
icon: Monitor,
},
];
function onAppearanceChange(value: string) {
form.appearance = value as AppearanceMode;
applyAppearance(form.appearance);
}
function submit() {
form.put('/admin/account/appearance', {
onError: () => {
toast.error('Gagal menyimpan tampilan.');
},
});
}
</script>
<template>
<Head title="Tampilan" />
<AccountLayout>
<form @submit.prevent="submit">
<Card>
<CardContent>
<FieldSet>
<FieldGroup>
<Field>
<FieldLabel>Mode Tampilan</FieldLabel>
<RadioGroup :model-value="form.appearance" class="grid gap-3"
@update:model-value="onAppearanceChange">
<Label v-for="option in options" :key="option.value"
:for="`appearance-${option.value}`" :class="cn(
'flex cursor-pointer items-start gap-3 rounded-lg border p-4 transition-colors',
form.appearance === option.value
? 'border-primary bg-primary/5'
: 'hover:bg-accent/50',
)">
<RadioGroupItem :id="`appearance-${option.value}`" :value="option.value"
class="mt-0.5" />
<div class="flex flex-1 items-start gap-3">
<component :is="option.icon"
class="text-muted-foreground mt-0.5 size-5 shrink-0" />
<div class="space-y-1">
<p class="text-sm leading-none font-medium">
{{ option.label }}
</p>
<p class="text-muted-foreground text-sm">
{{ option.description }}
</p>
</div>
</div>
</Label>
</RadioGroup>
<FieldError :errors="form.errors.appearance ? [form.errors.appearance] : []" />
</Field>
</FieldGroup>
</FieldSet>
<div class="mt-6 flex justify-end">
<Button type="submit" :disabled="form.processing">
<Save class="size-4" />
Simpan Tampilan
</Button>
</div>
</CardContent>
</Card>
</form>
</AccountLayout>
</template>