Adds frontend for cms page
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { PasswordField } from '@/components/ui/PasswordField';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { FormErrorBanner } from '@/components/ui/FormErrorBanner';
|
||||
import { FieldError } from '@/components/ui/FieldError';
|
||||
import { useAddCmsInstance } from '@/api/useAddCmsInstance';
|
||||
import { NetworkError, ProblemDetailsError } from '@/lib/api-client';
|
||||
import { addCmsInstanceSchema, type AddCmsInstanceFormData } from '@/lib/schemas/cms';
|
||||
|
||||
interface AddCmsInstanceDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function AddCmsInstanceDialog({ open, onOpenChange }: AddCmsInstanceDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [serverError, setServerError] = useState<string | null>(null);
|
||||
|
||||
const addInstance = useAddCmsInstance({
|
||||
onError: (err) => {
|
||||
if (err instanceof ProblemDetailsError && err.status === 409) {
|
||||
setServerError(t('cms.add.errors.conflict'));
|
||||
} else if (err instanceof NetworkError) {
|
||||
setServerError(t('errors.network'));
|
||||
} else {
|
||||
setServerError(t('errors.generic'));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm<AddCmsInstanceFormData>({
|
||||
resolver: zodResolver(addCmsInstanceSchema),
|
||||
mode: 'onTouched',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setServerError(null);
|
||||
addInstance.reset();
|
||||
reset();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open]);
|
||||
|
||||
const onSubmit = handleSubmit(async (values) => {
|
||||
setServerError(null);
|
||||
try {
|
||||
await addInstance.mutateAsync(values);
|
||||
toast.success(t('cms.add.successToast'));
|
||||
onOpenChange(false);
|
||||
} catch {
|
||||
// error shown via serverError state
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('cms.add.title')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={onSubmit} noValidate className="space-y-4">
|
||||
<FormErrorBanner
|
||||
error={serverError !== null ? { message: serverError } : null}
|
||||
onDismiss={() => setServerError(null)}
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="add-cms-name">{t('cms.add.nameLabel')}</Label>
|
||||
<Input
|
||||
id="add-cms-name"
|
||||
type="text"
|
||||
data-testid="add-cms-name"
|
||||
aria-invalid={errors.name !== undefined}
|
||||
{...register('name')}
|
||||
/>
|
||||
{errors.name && (
|
||||
<FieldError message={errors.name.message} testId="add-cms-name-error" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="add-cms-url">{t('cms.add.urlLabel')}</Label>
|
||||
<Input
|
||||
id="add-cms-url"
|
||||
type="text"
|
||||
placeholder="http://cms.example.com"
|
||||
data-testid="add-cms-url"
|
||||
aria-invalid={errors.url !== undefined}
|
||||
{...register('url')}
|
||||
/>
|
||||
{errors.url && (
|
||||
<FieldError message={errors.url.message} testId="add-cms-url-error" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="add-cms-apikey">{t('cms.add.apiKeyLabel')}</Label>
|
||||
<PasswordField
|
||||
id="add-cms-apikey"
|
||||
{...register('apiKey')}
|
||||
/>
|
||||
{errors.apiKey && (
|
||||
<FieldError message={errors.apiKey.message} testId="add-cms-apikey-error" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={isSubmitting || addInstance.isPending}
|
||||
data-testid="add-cms-submit"
|
||||
>
|
||||
{isSubmitting || addInstance.isPending ? '…' : t('cms.add.submitButton')}
|
||||
</Button>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user