feat(frontend): convert ExportPreviewModal to editable textarea with reset
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Copy, Download, Check } from 'lucide-react'
|
import { Copy, Download, Check, RotateCcw } from 'lucide-react'
|
||||||
import { Modal } from '@/components/common/Modal'
|
import { Modal } from '@/components/common/Modal'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
@@ -9,7 +9,9 @@ interface ExportPreviewModalProps {
|
|||||||
content: string
|
content: string
|
||||||
filename: string
|
filename: string
|
||||||
format: 'markdown' | 'text' | 'html' | 'psa'
|
format: 'markdown' | 'text' | 'html' | 'psa'
|
||||||
onDownload: () => void
|
onDownload: (content: string) => void
|
||||||
|
includeSummary?: boolean
|
||||||
|
onToggleSummary?: (include: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExportPreviewModal({
|
export function ExportPreviewModal({
|
||||||
@@ -19,12 +21,20 @@ export function ExportPreviewModal({
|
|||||||
filename,
|
filename,
|
||||||
format,
|
format,
|
||||||
onDownload,
|
onDownload,
|
||||||
|
includeSummary = false,
|
||||||
|
onToggleSummary,
|
||||||
}: ExportPreviewModalProps) {
|
}: ExportPreviewModalProps) {
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
const [editedContent, setEditedContent] = useState(content)
|
||||||
|
|
||||||
|
// Sync editedContent when content prop changes (new fetch / summary toggle)
|
||||||
|
useEffect(() => {
|
||||||
|
setEditedContent(content)
|
||||||
|
}, [content])
|
||||||
|
|
||||||
const handleCopy = async () => {
|
const handleCopy = async () => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(content)
|
await navigator.clipboard.writeText(editedContent)
|
||||||
setCopied(true)
|
setCopied(true)
|
||||||
setTimeout(() => setCopied(false), 2000)
|
setTimeout(() => setCopied(false), 2000)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -33,39 +43,79 @@ export function ExportPreviewModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleDownload = () => {
|
const handleDownload = () => {
|
||||||
onDownload()
|
onDownload(editedContent)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset copied state when modal closes
|
const handleReset = () => {
|
||||||
|
setEditedContent(content)
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setCopied(false)
|
setCopied(false)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isModified = editedContent !== content
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={handleClose} title="Export Preview" size="xl">
|
<Modal isOpen={isOpen} onClose={handleClose} title="Export Preview" size="xl">
|
||||||
{/* Filename and format info */}
|
{/* Filename, format info, and controls */}
|
||||||
<p className="mb-3 text-sm text-white/70">
|
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||||
Filename: <span className="font-mono text-white">{filename}</span>
|
<p className="text-sm text-white/70">
|
||||||
<span className="ml-3 rounded bg-white/10 px-2 py-0.5 text-xs text-white/70">
|
Filename: <span className="font-mono text-white">{filename}</span>
|
||||||
{format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : 'Plain Text'}
|
<span className="ml-3 rounded bg-white/10 px-2 py-0.5 text-xs text-white/70">
|
||||||
</span>
|
{format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : format === 'psa' ? 'PSA' : 'Plain Text'}
|
||||||
</p>
|
</span>
|
||||||
|
{isModified && (
|
||||||
{/* Content Preview */}
|
<span className="ml-2 text-xs text-yellow-400">(edited)</span>
|
||||||
<div
|
)}
|
||||||
className={cn(
|
</p>
|
||||||
'max-h-96 overflow-auto rounded-md border border-white/10 bg-black/50 p-4',
|
<div className="flex items-center gap-3">
|
||||||
'font-mono text-sm text-white'
|
{onToggleSummary && (
|
||||||
)}
|
<label className="flex items-center gap-2 text-sm text-white/60 cursor-pointer">
|
||||||
>
|
<input
|
||||||
<pre className="whitespace-pre-wrap">{content}</pre>
|
type="checkbox"
|
||||||
|
checked={includeSummary}
|
||||||
|
onChange={(e) => onToggleSummary(e.target.checked)}
|
||||||
|
className="h-4 w-4 rounded border-white/20 bg-black/50"
|
||||||
|
/>
|
||||||
|
Include Summary
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
{isModified && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleReset}
|
||||||
|
className="flex items-center gap-1 text-xs text-white/40 hover:text-white"
|
||||||
|
title="Reset to original"
|
||||||
|
>
|
||||||
|
<RotateCcw className="h-3 w-3" />
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Editable Content */}
|
||||||
|
<label htmlFor="export-content" className="sr-only">
|
||||||
|
Export content
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="export-content"
|
||||||
|
value={editedContent}
|
||||||
|
onChange={(e) => setEditedContent(e.target.value)}
|
||||||
|
className={cn(
|
||||||
|
'h-96 w-full resize-y rounded-md border border-white/10 bg-black/50 p-4',
|
||||||
|
'font-mono text-sm text-white',
|
||||||
|
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="mt-4 flex items-center justify-end gap-2">
|
<div className="mt-4 flex items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium',
|
'flex items-center gap-2 rounded-md border border-white/10 px-3 py-2 text-sm font-medium',
|
||||||
@@ -86,6 +136,7 @@ export function ExportPreviewModal({
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',
|
'flex items-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',
|
||||||
|
|||||||
Reference in New Issue
Block a user