113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import ImportLayout from '../../components/layout/ImportLayout';
|
|
import { Input } from '../../components/ui/Input';
|
|
import Button from '../../components/ui/Button';
|
|
|
|
export default function ImportFromUrlPage() {
|
|
const [importUrl, setImportUrl] = useState('');
|
|
const [scraping, setScraping] = useState(false);
|
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
|
|
|
const router = useRouter();
|
|
|
|
const handleImportFromUrl = async () => {
|
|
if (!importUrl.trim()) {
|
|
setErrors({ importUrl: 'URL is required' });
|
|
return;
|
|
}
|
|
|
|
setScraping(true);
|
|
setErrors({});
|
|
|
|
try {
|
|
const response = await fetch('/scrape/story', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ url: importUrl }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.error || 'Failed to scrape story');
|
|
}
|
|
|
|
const scrapedStory = await response.json();
|
|
|
|
// Redirect to add-story page with pre-filled data
|
|
const queryParams = new URLSearchParams({
|
|
from: 'url-import',
|
|
title: scrapedStory.title || '',
|
|
summary: scrapedStory.summary || '',
|
|
author: scrapedStory.author || '',
|
|
sourceUrl: scrapedStory.sourceUrl || importUrl,
|
|
tags: JSON.stringify(scrapedStory.tags || []),
|
|
content: scrapedStory.content || ''
|
|
});
|
|
|
|
router.push(`/add-story?${queryParams.toString()}`);
|
|
} catch (error: any) {
|
|
console.error('Failed to import story:', error);
|
|
setErrors({ importUrl: error.message });
|
|
} finally {
|
|
setScraping(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ImportLayout
|
|
title="Import Story from URL"
|
|
description="Import a single story from a website"
|
|
>
|
|
<div className="space-y-6">
|
|
<div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg p-6">
|
|
<h3 className="text-lg font-medium theme-header mb-4">Import Story from URL</h3>
|
|
<p className="theme-text text-sm mb-4">
|
|
Enter a URL from a supported story site to automatically extract the story content, title, author, and other metadata. After importing, you'll be able to review and edit the data before saving.
|
|
</p>
|
|
|
|
<div className="space-y-4">
|
|
<Input
|
|
label="Story URL"
|
|
type="url"
|
|
value={importUrl}
|
|
onChange={(e) => setImportUrl(e.target.value)}
|
|
placeholder="https://example.com/story-url"
|
|
error={errors.importUrl}
|
|
disabled={scraping}
|
|
/>
|
|
|
|
<div className="flex gap-3">
|
|
<Button
|
|
type="button"
|
|
onClick={handleImportFromUrl}
|
|
loading={scraping}
|
|
disabled={!importUrl.trim() || scraping}
|
|
>
|
|
{scraping ? 'Importing...' : 'Import Story'}
|
|
</Button>
|
|
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
href="/add-story"
|
|
disabled={scraping}
|
|
>
|
|
Enter Manually Instead
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="text-xs theme-text">
|
|
<p className="font-medium mb-1">Supported Sites:</p>
|
|
<p>Archive of Our Own, DeviantArt, FanFiction.Net, Literotica, Royal Road, Wattpad, and more</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ImportLayout>
|
|
);
|
|
} |