2026-01-06 14:17:55 +01:00

167 lines
5.4 KiB
TypeScript

import { useState } from 'react'
import { publishReview } from '@/lib/articleMutations'
import { nostrService } from '@/lib/nostr'
import { useNostrAuth } from '@/hooks/useNostrAuth'
import { t } from '@/lib/i18n'
import type { Article } from '@/types/nostr'
interface ReviewFormProps {
article: Article
onSuccess?: () => void
onCancel?: () => void
}
export function ReviewForm({ article, onSuccess, onCancel }: ReviewFormProps): React.ReactElement {
const { pubkey, connect } = useNostrAuth()
const [content, setContent] = useState('')
const [title, setTitle] = useState('')
const [text, setText] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleSubmit = async (e: React.FormEvent): Promise<void> => {
e.preventDefault()
if (!pubkey) {
await connect()
return
}
if (!content.trim()) {
setError(t('review.form.error.contentRequired'))
return
}
setLoading(true)
setError(null)
try {
const privateKey = nostrService.getPrivateKey()
if (!privateKey) {
setError(t('review.form.error.noPrivateKey'))
return
}
const category = article.category ?? 'science-fiction'
const seriesId = article.seriesId ?? ''
await publishReview({
articleId: article.id,
seriesId,
category,
authorPubkey: article.pubkey,
reviewerPubkey: pubkey,
content: content.trim(),
title: title.trim() || undefined,
text: text.trim() || undefined,
authorPrivateKey: privateKey,
})
setContent('')
setTitle('')
setText('')
onSuccess?.()
} catch (e) {
setError(e instanceof Error ? e.message : t('review.form.error.publishFailed'))
} finally {
setLoading(false)
}
}
if (!pubkey) {
return (
<div className="border border-neon-cyan/30 rounded-lg p-4 bg-cyber-dark">
<p className="text-cyber-accent mb-4">{t('review.form.connectRequired')}</p>
<button
onClick={() => {
void connect()
}}
className="px-4 py-2 bg-neon-green/20 hover:bg-neon-green/30 text-neon-green rounded-lg font-medium transition-all border border-neon-green/50"
>
{t('connect.connect')}
</button>
</div>
)
}
return (
<form onSubmit={handleSubmit} className="border border-neon-cyan/30 rounded-lg p-4 bg-cyber-dark space-y-4">
<h3 className="text-lg font-semibold text-neon-cyan">{t('review.form.title')}</h3>
<div>
<label htmlFor="review-title" className="block text-sm font-medium text-cyber-accent mb-1">
{t('review.form.title.label')} <span className="text-cyber-accent/50">({t('common.optional')})</span>
</label>
<input
id="review-title"
type="text"
value={title}
onChange={(e) => {
setTitle(e.target.value)
}}
placeholder={t('review.form.title.placeholder')}
className="w-full px-3 py-2 bg-cyber-darker border border-neon-cyan/30 rounded text-cyber-accent focus:border-neon-cyan focus:outline-none"
/>
</div>
<div>
<label htmlFor="review-content" className="block text-sm font-medium text-cyber-accent mb-1">
{t('review.form.content.label')} <span className="text-red-400">*</span>
</label>
<textarea
id="review-content"
value={content}
onChange={(e) => {
setContent(e.target.value)
}}
placeholder={t('review.form.content.placeholder')}
rows={6}
required
className="w-full px-3 py-2 bg-cyber-darker border border-neon-cyan/30 rounded text-cyber-accent focus:border-neon-cyan focus:outline-none"
/>
</div>
<div>
<label htmlFor="review-text" className="block text-sm font-medium text-cyber-accent mb-1">
{t('review.form.text.label')} <span className="text-cyber-accent/50">({t('common.optional')})</span>
</label>
<textarea
id="review-text"
value={text}
onChange={(e) => {
setText(e.target.value)
}}
placeholder={t('review.form.text.placeholder')}
rows={3}
className="w-full px-3 py-2 bg-cyber-darker border border-neon-cyan/30 rounded text-cyber-accent focus:border-neon-cyan focus:outline-none"
/>
<p className="text-xs text-cyber-accent/70 mt-1">{t('review.form.text.help')}</p>
</div>
{error && (
<div className="p-3 bg-red-900/20 border border-red-500/50 rounded text-red-400 text-sm">
{error}
</div>
)}
<div className="flex gap-2">
<button
type="submit"
disabled={loading}
className="px-4 py-2 bg-neon-green/20 hover:bg-neon-green/30 text-neon-green rounded-lg font-medium transition-all border border-neon-green/50 hover:shadow-glow-green disabled:opacity-50"
>
{loading ? t('common.loading') : t('review.form.submit')}
</button>
{onCancel && (
<button
type="button"
onClick={onCancel}
className="px-4 py-2 bg-cyber-darker hover:bg-cyber-dark text-cyber-accent rounded-lg font-medium transition-all border border-neon-cyan/30"
>
{t('common.cancel')}
</button>
)}
</div>
</form>
)
}