144 lines
3.9 KiB
TypeScript
144 lines
3.9 KiB
TypeScript
import React from 'react'
|
|
import { Button, ErrorState, Input, Textarea, Card } from '@/components/ui'
|
|
import { t } from '@/lib/i18n'
|
|
import type { ReviewFormController } from './useReviewFormController'
|
|
|
|
export function ReviewFormView(params: { ctrl: ReviewFormController; onCancel?: () => void }): React.ReactElement {
|
|
return (
|
|
<Card variant="default" className="space-y-4">
|
|
<form
|
|
onSubmit={(e) => void params.ctrl.handleSubmit(e)}
|
|
className="space-y-4"
|
|
>
|
|
<ReviewFormHeader />
|
|
<ReviewFormFields ctrl={params.ctrl} />
|
|
{params.ctrl.error ? <ErrorBox message={params.ctrl.error} /> : null}
|
|
<ReviewFormActions loading={params.ctrl.loading} onCancel={params.onCancel} />
|
|
</form>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function ReviewFormHeader(): React.ReactElement {
|
|
return <h3 className="text-lg font-semibold text-neon-cyan">{t('review.form.title')}</h3>
|
|
}
|
|
|
|
function ReviewFormFields(params: { ctrl: ReviewFormController }): React.ReactElement {
|
|
return (
|
|
<>
|
|
<TextInput
|
|
id="review-title"
|
|
label={t('review.form.title.label')}
|
|
value={params.ctrl.title}
|
|
onChange={params.ctrl.setTitle}
|
|
placeholder={t('review.form.title.placeholder')}
|
|
optionalLabel={`(${t('common.optional')})`}
|
|
/>
|
|
|
|
<TextAreaInput
|
|
id="review-content"
|
|
label={t('review.form.content.label')}
|
|
value={params.ctrl.content}
|
|
onChange={params.ctrl.setContent}
|
|
placeholder={t('review.form.content.placeholder')}
|
|
rows={6}
|
|
required
|
|
requiredMark
|
|
/>
|
|
|
|
<TextAreaInput
|
|
id="review-text"
|
|
label={t('review.form.text.label')}
|
|
value={params.ctrl.text}
|
|
onChange={params.ctrl.setText}
|
|
placeholder={t('review.form.text.placeholder')}
|
|
rows={3}
|
|
helpText={t('review.form.text.help')}
|
|
optionalLabel={`(${t('common.optional')})`}
|
|
/>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function ReviewFormActions(params: { loading: boolean; onCancel?: (() => void) | undefined }): React.ReactElement {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
type="submit"
|
|
variant="success"
|
|
disabled={params.loading}
|
|
loading={params.loading}
|
|
>
|
|
{params.loading ? t('common.loading') : t('review.form.submit')}
|
|
</Button>
|
|
{params.onCancel && (
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
onClick={params.onCancel}
|
|
>
|
|
{t('common.cancel')}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ErrorBox({ message }: { message: string }): React.ReactElement {
|
|
return <ErrorState message={message} />
|
|
}
|
|
|
|
function TextInput(params: {
|
|
id: string
|
|
label: string
|
|
value: string
|
|
onChange: (value: string) => void
|
|
placeholder: string
|
|
optionalLabel?: string
|
|
}): React.ReactElement {
|
|
const labelText = params.optionalLabel ? `${params.label} ${params.optionalLabel}` : params.label
|
|
return (
|
|
<Input
|
|
id={params.id}
|
|
type="text"
|
|
label={labelText}
|
|
value={params.value}
|
|
onChange={(e) => params.onChange(e.target.value)}
|
|
placeholder={params.placeholder}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function TextAreaInput(params: {
|
|
id: string
|
|
label: string
|
|
value: string
|
|
onChange: (value: string) => void
|
|
placeholder: string
|
|
rows: number
|
|
required?: boolean
|
|
requiredMark?: boolean
|
|
optionalLabel?: string
|
|
helpText?: string
|
|
}): React.ReactElement {
|
|
let labelText = params.label
|
|
if (params.requiredMark) {
|
|
labelText = `${labelText} *`
|
|
}
|
|
if (params.optionalLabel) {
|
|
labelText = `${labelText} ${params.optionalLabel}`
|
|
}
|
|
return (
|
|
<Textarea
|
|
id={params.id}
|
|
label={labelText}
|
|
value={params.value}
|
|
onChange={(e) => params.onChange(e.target.value)}
|
|
placeholder={params.placeholder}
|
|
rows={params.rows}
|
|
required={params.required}
|
|
{...(params.helpText ? { helperText: params.helpText } : {})}
|
|
/>
|
|
)
|
|
}
|