feat: add i18n and avatar upload

This commit is contained in:
Vaka.pro
2026-04-26 22:16:59 +03:00
parent db41d4a246
commit 1b23097b18
22 changed files with 750 additions and 145 deletions

View File

@@ -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>