Files
mini/src/components/admin/cartesian/LiveAssembler.tsx

120 lines
5.6 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { getDirectusClient, readItems } from '@/lib/directus/client';
import { Badge } from '@/components/ui/badge';
export default function LiveAssembler() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<any>({ sites: [], avatars: [], cities: [], templates: [] });
const [selections, setSelections] = useState({
siteId: '',
avatarId: '',
cityId: '', // Need to fetch cities intelligently (too many), for now fetch first 100
templateId: '',
niche: ''
});
const [preview, setPreview] = useState<any>(null);
useEffect(() => {
async function init() {
const client = getDirectusClient();
const [sites, avatars, cities, templates] = await Promise.all([
client.request(readItems('sites')),
client.request(readItems('avatars')),
client.request(readItems('geo_locations', { limit: 50 })), // Just sample
client.request(readItems('article_templates'))
]);
setData({ sites, avatars, cities, templates });
}
init();
}, []);
const handleGenerate = async () => {
setLoading(true);
try {
const res = await fetch('/api/preview-article', {
method: 'POST',
body: JSON.stringify(selections)
});
const json = await res.json();
if (json.error) throw new Error(json.error);
setPreview(json);
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};
return (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 h-[calc(100vh-200px)]">
{/* Controls */}
<Card className="col-span-1 border-r h-full overflow-y-auto">
<CardHeader>
<CardTitle>Assembler Settings</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<label className="text-xs uppercase font-bold text-slate-500">Site</label>
<select className="w-full p-2 border rounded mt-1"
onChange={e => setSelections({ ...selections, siteId: e.target.value })}>
<option value="">Select...</option>
{data.sites.map((s: any) => <option key={s.id} value={s.id}>{s.name}</option>)}
</select>
</div>
<div>
<label className="text-xs uppercase font-bold text-slate-500">Avatar</label>
<select className="w-full p-2 border rounded mt-1"
onChange={e => setSelections({ ...selections, avatarId: e.target.value })}>
<option value="">Select...</option>
{data.avatars.map((s: any) => <option key={s.id} value={s.id}>{s.base_name}</option>)}
</select>
</div>
<div>
<label className="text-xs uppercase font-bold text-slate-500">City (Sample)</label>
<select className="w-full p-2 border rounded mt-1"
onChange={e => setSelections({ ...selections, cityId: e.target.value })}>
<option value="">Select...</option>
{data.cities.map((s: any) => <option key={s.id} value={s.id}>{s.city}, {s.state}</option>)}
</select>
</div>
<div>
<label className="text-xs uppercase font-bold text-slate-500">Template</label>
<select className="w-full p-2 border rounded mt-1"
onChange={e => setSelections({ ...selections, templateId: e.target.value })}>
<option value="">Select...</option>
{data.templates.map((s: any) => <option key={s.id} value={s.id}>{s.name}</option>)}
</select>
</div>
<Button onClick={handleGenerate} disabled={loading} className="w-full">
{loading ? 'Assembling...' : 'Generate Preview'}
</Button>
</CardContent>
</Card>
{/* Preview Window */}
<div className="col-span-2 h-full overflow-y-auto bg-white border rounded-lg shadow-inner p-8">
{preview ? (
<div className="prose max-w-none">
<div className="mb-6 pb-6 border-b">
<h1 className="text-3xl font-bold text-slate-900 mb-2">{preview.title}</h1>
<div className="flex gap-2">
<Badge variant="secondary">Slug: {preview.slug}</Badge>
<Badge variant="outline">{preview.html_content.length} chars</Badge>
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: preview.html_content }} />
</div>
) : (
<div className="flex items-center justify-center h-full text-slate-400">
Configure setttings and click Generate to preview article.
</div>
)}
</div>
</div>
);
}