feat: add cover image upload functionality with loading state in article creation form

This commit is contained in:
2026-04-11 00:02:01 -03:00
parent 7943527106
commit d99ab44ec2
2 changed files with 32 additions and 5 deletions

View File

@@ -54,6 +54,7 @@ function validateContentFile(file: File): string | null {
export const CreateArticleForm = () => {
const [coverImageFile, setCoverImageFile] = useState<File | null>(null);
const [coverImageUploading, setCoverImageUploading] = useState(false);
const [contentFile, setContentFile] = useState<File | null>(null);
const coverImageUrlRef = useRef<string | null>(null);
@@ -78,10 +79,8 @@ export const CreateArticleForm = () => {
},
});
const title = useWatch({
control: form.control,
name: 'title',
});
const title = useWatch({ control: form.control, name: 'title' });
const coverImageUrl = useWatch({ control: form.control, name: 'coverImageUrl' });
useEffect(() => {
if (!title) return;
form.setValue('slug', slugify(title).toLowerCase());
@@ -93,6 +92,7 @@ export const CreateArticleForm = () => {
coverImageUrlRef.current = null;
}
setCoverImageFile(null);
setCoverImageUploading(false);
setContentFile(null);
}, []);
@@ -128,10 +128,13 @@ export const CreateArticleForm = () => {
setCoverImageFile(file);
if (!file) {
setCoverImageFile(null);
setCoverImageUploading(false);
form.setValue('coverImageUrl', '');
return;
}
setCoverImageUploading(true);
const fileMetadataResult = await uploadFile(file);
setCoverImageUploading(false);
if (!fileMetadataResult.ok) {
setCoverImageFile(null);
form.setValue('coverImageUrl', '');
@@ -255,6 +258,8 @@ export const CreateArticleForm = () => {
label='Cover image'
description='PNG, JPG, GIF, WebP accepted'
error={form.formState.errors.coverImageUrl?.message}
previewUrl={coverImageUrl || undefined}
isUploading={coverImageUploading}
icon={
<Image
src={ImageLogo}

View File

@@ -6,6 +6,7 @@ import {
FieldDescription,
FieldError,
} from '@/ui/components/shadcn/field';
import { Spinner } from '@/ui/components/shadcn/spinner';
import {
FileUpload,
FileUploadDropzone,
@@ -29,6 +30,8 @@ export interface FileUploadFieldProps {
description?: string;
error?: string;
icon?: React.ReactNode;
previewUrl?: string;
isUploading?: boolean;
}
export const FileUploadField: React.FC<FileUploadFieldProps> = ({
@@ -41,6 +44,8 @@ export const FileUploadField: React.FC<FileUploadFieldProps> = ({
description,
error,
icon,
previewUrl,
isUploading,
}) => {
const handleAccept = useCallback(
(files: File[]) => {
@@ -90,7 +95,24 @@ export const FileUploadField: React.FC<FileUploadFieldProps> = ({
<FileUploadList>
{file && (
<FileUploadItem value={file}>
<FileUploadItemPreview />
<FileUploadItemPreview
render={
isUploading
? () => (
<Spinner className='size-5 text-muted-foreground' />
)
: previewUrl
? () => (
// eslint-disable-next-line @next/next/no-img-element
<img
src={previewUrl}
alt='Uploaded image'
className='size-full object-cover'
/>
)
: undefined
}
/>
<FileUploadItemMetadata size='sm' />
<FileUploadItemDelete asChild>
<Button