lms/resources/js/pages/dashboard/blogs/partials/blog-form.tsx
2025-12-15 12:26:23 +01:00

188 lines
7.5 KiB
TypeScript

import Combobox from '@/components/combobox';
import InputError from '@/components/input-error';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { onHandleChange } from '@/lib/inertia';
import { Link, useForm, usePage } from '@inertiajs/react';
import { FileText, Image, Save } from 'lucide-react';
import { FormEvent, useState } from 'react';
import { Editor } from 'richtor';
import 'richtor/styles';
import { BlogCreateEditProps } from '../create-edit';
const BlogForm = () => {
const { props } = usePage<BlogCreateEditProps>();
const { auth, blog, categories, statuses, translate } = props;
const { dashboard, input, button } = translate;
const [banner, setBanner] = useState(blog?.banner || '/assets/images/blank-image.jpg');
const [thumbnail, setThumbnail] = useState(blog?.thumbnail || '/assets/images/blank-image.jpg');
const { data, setData, post, processing, errors } = useForm({
title: blog ? blog.title : '',
slug: blog ? blog.slug : '',
description: blog ? blog.description : '',
keywords: blog ? blog.keywords || '' : '',
status: blog ? blog.status : 'draft',
thumbnail: null,
banner: null,
user_id: blog ? blog.user_id : auth.user.id,
blog_category_id: blog ? blog.blog_category_id : '',
});
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
if (blog) {
post(route('blogs.update', blog.id));
} else {
post(route('blogs.store'));
}
};
const transformedCategories = categories?.map((category) => ({
label: category.name,
value: category.id as string,
}));
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* Basic Information */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="h-5 w-5" />
{dashboard.blog_information}
</CardTitle>
<CardDescription>{dashboard.provide_blog_details}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="title">{dashboard.title_80_char}</Label>
<Input
id="title"
value={data.title}
onChange={(e) => setData('title', e.target.value)}
placeholder={dashboard.enter_blog_title}
maxLength={80}
/>
<InputError message={errors.title} />
</div>
<div className="grid gap-4 md:grid-cols-2">
<div>
<Label htmlFor="blog_category_id">{input.category} *</Label>
<Combobox
defaultValue={data.blog_category_id as string}
data={transformedCategories || []}
placeholder={dashboard.select_category}
onSelect={(selected) => setData('blog_category_id', selected.value)}
/>
<InputError message={errors.blog_category_id} />
</div>
<div>
<Label htmlFor="status">{input.status} *</Label>
<Select value={data.status} onValueChange={(value) => setData('status', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(statuses).map(([key, label]) => (
<SelectItem key={key} value={key}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
<InputError message={errors.status} />
</div>
</div>
<div>
<Label htmlFor="keywords">{dashboard.keywords_80_char}</Label>
<Input
id="keywords"
value={data.keywords}
onChange={(e) => setData('keywords', e.target.value)}
placeholder={dashboard.enter_your_keywords}
maxLength={80}
/>
<InputError message={errors.keywords} />
</div>
<div>
<Label htmlFor="description">{input.description} *</Label>
<Editor
ssr={true}
output="html"
placeholder={{
paragraph: dashboard.write_blog_content_here,
imageCaption: dashboard.type_caption_optional,
}}
contentMinHeight={256}
contentMaxHeight={640}
initialContent={data.description}
onContentChange={(value: any) =>
setData((prev) => ({
...prev,
description: value as string,
}))
}
/>
<InputError message={errors.description} />
</div>
</CardContent>
</Card>
{/* Media Information */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Image className="h-5 w-5" />
{dashboard.media_files}
</CardTitle>
<CardDescription>{dashboard.upload_banner_thumbnail_desc}</CardDescription>
</CardHeader>
<CardContent className="grid gap-4 md:grid-cols-2">
<div>
<Label htmlFor="banner">{dashboard.blog_banner}</Label>
<Input id="banner" type="file" accept="image/*" name="banner" onChange={(e) => onHandleChange(e, setData, setBanner)} />
<InputError message={errors.banner} />
<div className="border-border mt-3 overflow-hidden rounded-lg border-2 border-dashed">
<img src={banner} alt="" />
</div>
</div>
<div>
<Label htmlFor="thumbnail">{dashboard.blog_thumbnail}</Label>
<Input id="thumbnail" type="file" accept="image/*" name="thumbnail" onChange={(e) => onHandleChange(e, setData, setThumbnail)} />
<InputError message={errors.thumbnail} />
<div className="border-border mt-3 overflow-hidden rounded-lg border-2 border-dashed">
<img src={thumbnail} alt="" />
</div>
</div>
</CardContent>
</Card>
{/* Submit Buttons */}
<div className="flex items-center justify-end gap-4">
<Button type="button" variant="outline" asChild>
<Link href={route('blogs.index')}>{button.cancel}</Link>
</Button>
<Button type="submit" disabled={processing}>
<Save className="mr-2 h-4 w-4" />
{blog ? dashboard.update_blog : dashboard.add_blog}
</Button>
</div>
</form>
);
};
export default BlogForm;