2026-01-08 23:53:05 +01:00

221 lines
7.4 KiB
TypeScript

import React from 'react'
import { ImageUploadField } from '../ImageUploadField'
import { t } from '@/lib/i18n'
import type { SeriesDraft } from './createSeriesModalTypes'
import type { CreateSeriesModalController } from './useCreateSeriesModalController'
export function CreateSeriesModalView({ ctrl }: { ctrl: CreateSeriesModalController }): React.ReactElement {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
<div className="bg-cyber-dark border border-neon-cyan/30 rounded-lg p-6 max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
<CreateSeriesModalHeader loading={ctrl.loading} onClose={ctrl.handleClose} />
{!ctrl.canPublish ? <NotAuthorWarning /> : null}
<CreateSeriesForm ctrl={ctrl} />
</div>
</div>
)
}
function CreateSeriesModalHeader({ loading, onClose }: { loading: boolean; onClose: () => void }): React.ReactElement {
return (
<div className="flex items-center justify-between mb-6">
<h2 className="text-2xl font-semibold text-neon-cyan">{t('series.create.title')}</h2>
<button
type="button"
onClick={onClose}
disabled={loading}
className="text-cyber-accent hover:text-neon-cyan transition-colors disabled:opacity-50"
>
</button>
</div>
)
}
function NotAuthorWarning(): React.ReactElement {
return (
<div className="mb-4 p-4 bg-yellow-900/30 border border-yellow-500/50 rounded text-yellow-300">
<p>{t('series.create.error.notAuthor')}</p>
</div>
)
}
function CreateSeriesForm({ ctrl }: { ctrl: CreateSeriesModalController }): React.ReactElement {
return (
<form onSubmit={(e) => void ctrl.handleSubmit(e)} className="space-y-4">
<SeriesTextFields draft={ctrl.draft} setDraft={ctrl.setDraft} loading={ctrl.loading} canPublish={ctrl.canPublish} />
<SeriesCategoryField draft={ctrl.draft} setDraft={ctrl.setDraft} loading={ctrl.loading} canPublish={ctrl.canPublish} />
<SeriesCoverField draft={ctrl.draft} setDraft={ctrl.setDraft} />
<SeriesError error={ctrl.error} />
<SeriesActions loading={ctrl.loading} canPublish={ctrl.canPublish} onClose={ctrl.handleClose} />
</form>
)
}
function SeriesTextFields(params: {
draft: SeriesDraft
setDraft: (draft: SeriesDraft) => void
loading: boolean
canPublish: boolean
}): React.ReactElement {
const disabled = params.loading || !params.canPublish
return (
<>
<TextField
id="series-title"
label={t('series.create.field.title')}
value={params.draft.title}
disabled={disabled}
required
onChange={(value) => params.setDraft({ ...params.draft, title: value })}
/>
<TextAreaField
id="series-description"
label={t('series.create.field.description')}
value={params.draft.description}
disabled={disabled}
required
rows={4}
onChange={(value) => params.setDraft({ ...params.draft, description: value })}
/>
<TextAreaField
id="series-preview"
label={t('series.create.field.preview')}
value={params.draft.preview}
disabled={disabled}
required
rows={3}
helpText={t('series.create.field.preview.help')}
onChange={(value) => params.setDraft({ ...params.draft, preview: value })}
/>
</>
)
}
function SeriesCategoryField(params: {
draft: SeriesDraft
setDraft: (draft: SeriesDraft) => void
loading: boolean
canPublish: boolean
}): React.ReactElement {
const disabled = params.loading || !params.canPublish
return (
<div>
<label htmlFor="series-category" className="block text-sm font-medium text-neon-cyan mb-2">
{t('series.create.field.category')}
</label>
<select
id="series-category"
value={params.draft.category}
onChange={(e) => params.setDraft({ ...params.draft, category: e.target.value as SeriesDraft['category'] })}
className="w-full px-3 py-2 bg-cyber-darker border border-cyber-accent/30 rounded text-cyber-light focus:border-neon-cyan focus:outline-none"
required
disabled={disabled}
>
<option value="science-fiction">{t('category.science-fiction')}</option>
<option value="scientific-research">{t('category.scientific-research')}</option>
</select>
</div>
)
}
function SeriesCoverField({ draft, setDraft }: { draft: SeriesDraft; setDraft: (draft: SeriesDraft) => void }): React.ReactElement {
return (
<ImageUploadField
id="series-cover"
label={t('series.create.field.cover')}
value={draft.coverUrl}
onChange={(url) => setDraft({ ...draft, coverUrl: url })}
helpText={t('series.create.field.cover.help')}
/>
)
}
function SeriesError({ error }: { error: string | null }): React.ReactElement | null {
if (!error) {
return null
}
return (
<div className="p-4 bg-red-900/30 border border-red-500/50 rounded text-red-300">
<p>{error}</p>
</div>
)
}
function SeriesActions(params: { loading: boolean; canPublish: boolean; onClose: () => void }): React.ReactElement {
return (
<div className="flex items-center justify-end gap-4 pt-4">
<button
type="button"
onClick={params.onClose}
disabled={params.loading}
className="px-4 py-2 bg-cyber-darker border border-cyber-accent/30 rounded text-cyber-light hover:border-neon-cyan transition-colors disabled:opacity-50"
>
{t('common.cancel')}
</button>
<button
type="submit"
disabled={params.loading || !params.canPublish}
className="px-4 py-2 bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded-lg font-medium transition-all border border-neon-cyan/50 hover:shadow-glow-cyan disabled:opacity-50 disabled:cursor-not-allowed"
>
{params.loading ? t('common.loading') : t('series.create.submit')}
</button>
</div>
)
}
function TextField(params: {
id: string
label: string
value: string
disabled: boolean
required: boolean
onChange: (value: string) => void
}): React.ReactElement {
return (
<div>
<label htmlFor={params.id} className="block text-sm font-medium text-neon-cyan mb-2">
{params.label}
</label>
<input
id={params.id}
type="text"
value={params.value}
onChange={(e) => params.onChange(e.target.value)}
className="w-full px-3 py-2 bg-cyber-darker border border-cyber-accent/30 rounded text-cyber-light focus:border-neon-cyan focus:outline-none"
required={params.required}
disabled={params.disabled}
/>
</div>
)
}
function TextAreaField(params: {
id: string
label: string
value: string
disabled: boolean
required: boolean
rows: number
helpText?: string
onChange: (value: string) => void
}): React.ReactElement {
return (
<div>
<label htmlFor={params.id} className="block text-sm font-medium text-neon-cyan mb-2">
{params.label}
</label>
<textarea
id={params.id}
value={params.value}
onChange={(e) => params.onChange(e.target.value)}
rows={params.rows}
className="w-full px-3 py-2 bg-cyber-darker border border-cyber-accent/30 rounded text-cyber-light focus:border-neon-cyan focus:outline-none"
required={params.required}
disabled={params.disabled}
/>
{params.helpText ? <p className="text-xs text-cyber-accent/70 mt-1">{params.helpText}</p> : null}
</div>
)
}