feat: add i18n and avatar upload
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
useUpdateWish,
|
||||
useUploadWishImage,
|
||||
} from '@/features/wishes/wishes.hooks';
|
||||
import { translateValidation, useI18n } from '@/i18n/i18n';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
@@ -24,6 +25,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
const { t } = useI18n();
|
||||
const create = useCreateWish();
|
||||
const update = useUpdateWish();
|
||||
const upload = useUploadWishImage();
|
||||
@@ -78,59 +80,74 @@ export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={mode === 'create' ? 'Add a wish' : 'Edit wish'}
|
||||
title={mode === 'create' ? t('wishForm.addTitle') : t('wishForm.editTitle')}
|
||||
description={
|
||||
mode === 'create'
|
||||
? 'Tell us what you want. A link helps us grab a preview image automatically.'
|
||||
: 'Update the details of your wish.'
|
||||
? t('wishForm.addDescription')
|
||||
: t('wishForm.editDescription')
|
||||
}
|
||||
size="lg"
|
||||
footer={
|
||||
<>
|
||||
<Button variant="ghost" onClick={onClose}>
|
||||
Cancel
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button type="submit" form="wish-form" disabled={isSubmitting}>
|
||||
{isSubmitting && <Loader2 className="h-4 w-4 animate-spin" />}
|
||||
{mode === 'create' ? 'Add wish' : 'Save'}
|
||||
{mode === 'create' ? t('wishForm.addSubmit') : t('common.save')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<form id="wish-form" className="grid gap-4" onSubmit={submit}>
|
||||
<div className="field">
|
||||
<Label htmlFor="title">Title</Label>
|
||||
<Input id="title" placeholder="Moka pot, size 3" {...register('title')} />
|
||||
{errors.title && <span className="field__error">{errors.title.message}</span>}
|
||||
<Label htmlFor="title">{t('wishForm.title')}</Label>
|
||||
<Input id="title" placeholder={t('wishForm.titlePlaceholder')} {...register('title')} />
|
||||
{errors.title && (
|
||||
<span className="field__error">{translateValidation(t, errors.title.message)}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-[1fr_auto]">
|
||||
<div className="field">
|
||||
<Label htmlFor="price">Price (optional)</Label>
|
||||
<Input id="price" placeholder="e.g. 2490" inputMode="decimal" {...register('price')} />
|
||||
{errors.price && <span className="field__error">{errors.price.message as string}</span>}
|
||||
<Label htmlFor="price">{t('wishForm.price')}</Label>
|
||||
<Input
|
||||
id="price"
|
||||
placeholder={t('wishForm.pricePlaceholder')}
|
||||
inputMode="decimal"
|
||||
{...register('price')}
|
||||
/>
|
||||
{errors.price && (
|
||||
<span className="field__error">
|
||||
{translateValidation(t, errors.price.message as string)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="field">
|
||||
<Label htmlFor="currency">Currency</Label>
|
||||
<Label htmlFor="currency">{t('wishForm.currency')}</Label>
|
||||
<Input id="currency" maxLength={3} className="uppercase w-24" {...register('currency')} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<Label htmlFor="url">Link (optional)</Label>
|
||||
<Label htmlFor="url">{t('wishForm.link')}</Label>
|
||||
<Input id="url" type="url" placeholder="https://..." {...register('url')} />
|
||||
{errors.url && <span className="field__error">{errors.url.message as string}</span>}
|
||||
{errors.url && (
|
||||
<span className="field__error">
|
||||
{translateValidation(t, errors.url.message as string)}
|
||||
</span>
|
||||
)}
|
||||
<p className="text-xs text-muted">
|
||||
We will try to pull a preview image from the link after saving.
|
||||
{t('wishForm.linkHint')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<Label htmlFor="comment">Comment (optional)</Label>
|
||||
<Label htmlFor="comment">{t('wishForm.comment')}</Label>
|
||||
<Textarea
|
||||
id="comment"
|
||||
rows={3}
|
||||
placeholder="Size / color / notes..."
|
||||
placeholder={t('wishForm.commentPlaceholder')}
|
||||
{...register('comment')}
|
||||
/>
|
||||
</div>
|
||||
@@ -140,7 +157,7 @@ export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
<section className="mt-6 rounded-md border border-border bg-surface-muted p-4">
|
||||
<div className="mb-3 flex items-center gap-2 text-sm font-medium text-ink">
|
||||
<ImageIcon className="h-4 w-4" />
|
||||
Image
|
||||
{t('wishForm.image')}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<input
|
||||
@@ -165,7 +182,7 @@ export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
) : (
|
||||
<Upload className="h-4 w-4" />
|
||||
)}
|
||||
Upload custom
|
||||
{t('wishForm.uploadCustom')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -178,7 +195,7 @@ export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
) : (
|
||||
<RefreshCcw className="h-4 w-4" />
|
||||
)}
|
||||
Refresh from link
|
||||
{t('wishForm.refreshFromLink')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -187,7 +204,7 @@ export function WishForm({ open, mode, initial, onClose }: Props) {
|
||||
disabled={resetImage.isPending}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
Reset to default
|
||||
{t('wishForm.resetImage')}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user