Spaces:
Sleeping
Sleeping
| 'use client'; | |
| import { useState, useEffect } from 'react'; | |
| import { ArticleGenerationModal, ArticleGenerationParams } from './ArticleGenerationModal'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Checkbox } from '@/components/ui/checkbox'; | |
| import { Label } from '@/components/ui/label'; | |
| import { Textarea } from '@/components/ui/textarea'; | |
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; | |
| interface ArticleProps { | |
| className?: string; | |
| sourceArticle: string; | |
| onSourceArticleChange: (text: string) => void; | |
| isSourceLocked: boolean; | |
| onSourceLockedChange: (locked: boolean) => void; | |
| } | |
| export function Article({ | |
| className = '', | |
| sourceArticle, | |
| onSourceArticleChange, | |
| isSourceLocked, | |
| onSourceLockedChange, | |
| }: ArticleProps) { | |
| const [wordCount, setWordCount] = useState(0); | |
| const [isModalOpen, setIsModalOpen] = useState(false); | |
| const [isGeneratingArticle, setIsGeneratingArticle] = useState(false); | |
| const [isExpandOpen, setIsExpandOpen] = useState(false); | |
| const updateWordCount = (text: string) => { | |
| const words = text.trim().split(/\s+/).length; | |
| setWordCount(text.trim() === '' ? 0 : words); | |
| }; | |
| useEffect(() => { | |
| updateWordCount(sourceArticle); | |
| }, [sourceArticle]); | |
| const handleSourceArticleChange = (text: string) => { | |
| if (!isSourceLocked) { | |
| onSourceArticleChange(text); | |
| } | |
| }; | |
| const handleGenerateArticle = async (params: ArticleGenerationParams) => { | |
| setIsGeneratingArticle(true); | |
| try { | |
| // Call the appropriate API: rewrite when inputArticle is provided, otherwise generate | |
| const endpoint = params.inputArticle && params.inputArticle.trim().length > 0 | |
| ? '/api/article-rewrite' | |
| : '/api/generate-article'; | |
| const response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(params), | |
| }); | |
| if (response.ok) { | |
| const { article } = await response.json(); | |
| onSourceArticleChange(article); | |
| setIsModalOpen(false); | |
| } else { | |
| console.error('Failed to generate article'); | |
| } | |
| } catch (error) { | |
| console.error('Error generating article:', error); | |
| } finally { | |
| setIsGeneratingArticle(false); | |
| } | |
| }; | |
| return ( | |
| <Card className={`flex flex-col h-full ${className}`}> | |
| <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-4 px-6 pt-2"> | |
| <CardTitle className="text-lg font-semibold"> | |
| π Source Article | |
| </CardTitle> | |
| <div className="flex items-center space-x-4"> | |
| <Button | |
| onClick={() => setIsModalOpen(true)} | |
| disabled={isSourceLocked} | |
| size="sm" | |
| > | |
| β¨ Article Generate / Rewrite | |
| </Button> | |
| <span className="text-sm text-muted-foreground"> | |
| {wordCount} words | |
| </span> | |
| <div className="flex items-center space-x-2"> | |
| <Checkbox | |
| id="lock-source" | |
| checked={isSourceLocked} | |
| onCheckedChange={onSourceLockedChange} | |
| /> | |
| <Label htmlFor="lock-source" className="text-sm cursor-pointer"> | |
| π Lock | |
| </Label> | |
| </div> | |
| <Button | |
| onClick={() => setIsExpandOpen(true)} | |
| disabled={isSourceLocked} | |
| variant="outline" | |
| size="sm" | |
| > | |
| β€’ Expand | |
| </Button> | |
| </div> | |
| </CardHeader> | |
| <CardContent className="flex-1 px-6 pb-6 overflow-hidden"> | |
| <Textarea | |
| value={sourceArticle} | |
| onChange={(e) => handleSourceArticleChange(e.target.value)} | |
| placeholder="Paste or type your source article here (articles, passages, etc.)..." | |
| disabled={isSourceLocked} | |
| className="w-full h-full resize-none" | |
| /> | |
| </CardContent> | |
| <ArticleGenerationModal | |
| isOpen={isModalOpen} | |
| onClose={() => setIsModalOpen(false)} | |
| onGenerate={handleGenerateArticle} | |
| isGenerating={isGeneratingArticle} | |
| /> | |
| {/* Full-screen editor dialog for comfortable editing */} | |
| <Dialog open={isExpandOpen} onOpenChange={setIsExpandOpen}> | |
| <DialogContent className="w-[98vw] sm:max-w-none md:max-w-[90vw] h-[70vh] max-h-[95vh] p-0 flex flex-col"> | |
| <DialogHeader className="px-6 pt-6 pb-2"> | |
| <DialogTitle>π Edit Source Article</DialogTitle> | |
| </DialogHeader> | |
| <div className="flex-1 px-6 pb-6"> | |
| <Textarea | |
| value={sourceArticle} | |
| onChange={(e) => handleSourceArticleChange(e.target.value)} | |
| placeholder="Paste or type your source article here (articles, passages, etc.)..." | |
| disabled={isSourceLocked} | |
| className="w-full h-full resize-none" | |
| /> | |
| </div> | |
| </DialogContent> | |
| </Dialog> | |
| </Card> | |
| ); | |
| } |