- **Motivations :** Assurer passage du lint strict et clarifier la logique paiements/publications. - **Root causes :** Fonctions trop longues, promesses non gérées et typages WebLN/Nostr incomplets. - **Correctifs :** Refactor PaymentModal (handlers void), extraction helpers articlePublisher, simplification polling sponsoring/zap, corrections curly et awaits. - **Evolutions :** Nouveau module articlePublisherHelpers pour présentation/aiguillage contenu privé. - **Page affectées :** components/PaymentModal.tsx, lib/articlePublisher.ts, lib/articlePublisherHelpers.ts, lib/paymentPolling.ts, lib/sponsoring.ts, lib/nostrZapVerification.ts et dépendances liées.
129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
import React from 'react'
|
|
import type { ArticleDraft } from '@/lib/articlePublisher'
|
|
import { ArticleField } from './ArticleField'
|
|
import { ArticleFormButtons } from './ArticleFormButtons'
|
|
import { CategorySelect } from './CategorySelect'
|
|
|
|
interface ArticleEditorFormProps {
|
|
draft: ArticleDraft
|
|
onDraftChange: (draft: ArticleDraft) => void
|
|
onSubmit: (e: React.FormEvent) => void
|
|
loading: boolean
|
|
error: string | null
|
|
onCancel?: () => void
|
|
}
|
|
|
|
function CategoryField({
|
|
value,
|
|
onChange,
|
|
}: {
|
|
value: ArticleDraft['category']
|
|
onChange: (value: ArticleDraft['category']) => void
|
|
}) {
|
|
return (
|
|
<CategorySelect
|
|
id="category"
|
|
label="Catégorie"
|
|
value={value}
|
|
onChange={onChange}
|
|
required
|
|
helpText="Sélectionnez la catégorie de votre article"
|
|
/>
|
|
)
|
|
}
|
|
|
|
function ErrorAlert({ error }: { error: string | null }) {
|
|
if (!error) {
|
|
return null
|
|
}
|
|
return (
|
|
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
|
<p className="text-sm text-red-800">{error}</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const ArticleFieldsLeft = ({
|
|
draft,
|
|
onDraftChange,
|
|
}: {
|
|
draft: ArticleDraft
|
|
onDraftChange: (draft: ArticleDraft) => void
|
|
}) => (
|
|
<div className="space-y-4">
|
|
<CategoryField value={draft.category} onChange={(value) => onDraftChange({ ...draft, category: value })} />
|
|
<ArticleField
|
|
id="title"
|
|
label="Titre"
|
|
value={draft.title}
|
|
onChange={(value) => onDraftChange({ ...draft, title: value as string })}
|
|
required
|
|
placeholder="Entrez le titre de l'article"
|
|
/>
|
|
<ArticleField
|
|
id="preview"
|
|
label="Aperçu (Public)"
|
|
value={draft.preview}
|
|
onChange={(value) => onDraftChange({ ...draft, preview: value as string })}
|
|
required
|
|
type="textarea"
|
|
rows={4}
|
|
placeholder="Cet aperçu sera visible par tous gratuitement"
|
|
helpText="Ce contenu sera visible par tous"
|
|
/>
|
|
</div>
|
|
)
|
|
|
|
const ArticleFieldsRight = ({
|
|
draft,
|
|
onDraftChange,
|
|
}: {
|
|
draft: ArticleDraft
|
|
onDraftChange: (draft: ArticleDraft) => void
|
|
}) => (
|
|
<div className="space-y-4">
|
|
<ArticleField
|
|
id="content"
|
|
label="Contenu complet (Privé)"
|
|
value={draft.content}
|
|
onChange={(value) => onDraftChange({ ...draft, content: value as string })}
|
|
required
|
|
type="textarea"
|
|
rows={8}
|
|
placeholder="Ce contenu sera chiffré et envoyé aux lecteurs qui paient"
|
|
helpText="Ce contenu sera chiffré et envoyé comme message privé après paiement"
|
|
/>
|
|
<ArticleField
|
|
id="zapAmount"
|
|
label="Prix (sats)"
|
|
value={draft.zapAmount}
|
|
onChange={(value) => onDraftChange({ ...draft, zapAmount: value as number })}
|
|
required
|
|
type="number"
|
|
min={1}
|
|
helpText="Montant en satoshis pour débloquer le contenu complet"
|
|
/>
|
|
</div>
|
|
)
|
|
|
|
export function ArticleEditorForm({
|
|
draft,
|
|
onDraftChange,
|
|
onSubmit,
|
|
loading,
|
|
error,
|
|
onCancel,
|
|
}: ArticleEditorFormProps) {
|
|
return (
|
|
<form onSubmit={onSubmit} className="border rounded-lg p-6 bg-white space-y-4">
|
|
<h2 className="text-2xl font-bold mb-4">Publier un nouvel article</h2>
|
|
<div className="space-y-4">
|
|
<ArticleFieldsLeft draft={draft} onDraftChange={onDraftChange} />
|
|
<ArticleFieldsRight draft={draft} onDraftChange={onDraftChange} />
|
|
</div>
|
|
<ErrorAlert error={error} />
|
|
<ArticleFormButtons loading={loading} onCancel={onCancel} />
|
|
</form>
|
|
)
|
|
}
|