Collections.jsx
import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { apiGet, apiPost, apiDelete } from '../hooks/useAPI.js';
import { useChat } from '../layouts/AdminLayout.jsx';
import { TiltCard } from '../components/TiltCard.jsx';
export function Collections() {
const { toggleChat } = useChat() || {};
const navigate = useNavigate();
const [collections, setCollections] = useState([]);
const [loading, setLoading] = useState(true);
const [showModal, setShowModal] = useState(false);
const [creating, setCreating] = useState(false);
const fetchCollections = useCallback(() => {
setLoading(true);
apiGet('/api/admin/collections')
.then((data) => setCollections(data || []))
.finally(() => setLoading(false));
}, []);
useEffect(() => {
fetchCollections();
}, [fetchCollections]);
const handleCreate = async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
setCreating(true);
try {
const col = await apiPost('/api/admin/collections', {
name: formData.get('name'),
description: formData.get('description'),
schema: '[]',
});
setShowModal(false);
form.reset();
navigate(`/collections/${col.id}`);
} finally {
setCreating(false);
}
};
const deleteCollection = async (id, e) => {
e.preventDefault();
e.stopPropagation();
if (!confirm('Delete this collection and all its documents?')) return;
await apiDelete(`/api/admin/collections/${id}`);
fetchCollections();
};
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-base-300 bg-base-200/50">
<div>
<h2 className="font-semibold">Collections</h2>
<p className="text-xs text-base-content/50">Dynamic data</p>
</div>
<div className="flex items-center gap-2">
<button onClick={() => setShowModal(true)} className="btn btn-ghost btn-sm gap-1">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v16m8-8H4" />
</svg>
Collection
</button>
{toggleChat && (
<button onClick={toggleChat} className="btn btn-soft btn-primary btn-sm gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
AI Assistant
</button>
)}
</div>
</div>
{/* Content */}
<div className="flex-1 overflow-auto p-4">
{loading ? (
<div className="flex justify-center py-12">
<span className="loading loading-spinner loading-lg" />
</div>
) : collections.length ? (
<div className="space-y-2 max-w-2xl mx-auto">
{collections.map((col) => (
<TiltCard
key={col.id}
className="block rounded-lg bg-base-100/80 backdrop-blur-sm border border-white/5 hover:shadow-lg hover:border-white/10 cursor-pointer border-l-4 border-l-secondary"
>
<Link to={`/collections/${col.id}`} className="block p-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-secondary/15 flex items-center justify-center flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
</svg>
</div>
<div>
<span className="font-semibold text-base-content">{col.name}</span>
<div className="text-xs text-base-content/40">{col.documentCount || 0} documents{col.system && ' · System'}</div>
</div>
</div>
<div className="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/30" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
{col.description && (
<p className="text-sm text-base-content/50 mt-1 ml-11 line-clamp-1">{col.description}</p>
)}
</Link>
</TiltCard>
))}
</div>
) : (
<div className="flex flex-col items-center justify-center h-full text-center px-6">
<div className="w-16 h-16 rounded-2xl bg-base-200 flex items-center justify-center mb-4">
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-base-content/30" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
</svg>
</div>
<h3 className="text-lg font-semibold mb-1">No collections yet</h3>
<p className="text-base-content/50 text-sm mb-4">Collections help you organize dynamic content like blog posts, products, etc.</p>
<button onClick={() => setShowModal(true)} className="btn btn-primary btn-sm">Create First Collection</button>
</div>
)}
</div>
{/* New Collection Modal */}
{showModal && (
<dialog className="modal modal-open">
<div className="modal-box w-11/12 max-w-lg">
<button className="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" onClick={() => setShowModal(false)}>✕</button>
<h3 className="font-bold text-lg mb-4">New Collection</h3>
<form onSubmit={handleCreate} className="space-y-4">
<div className="form-control">
<label className="floating-label">
<span>Name <span className="text-error">*</span></span>
<input type="text" name="name" placeholder="Blog Posts" className="input input-bordered w-full" required autoFocus />
</label>
</div>
<div className="form-control">
<label className="floating-label">
<span>Description</span>
<input type="text" name="description" placeholder="A collection of blog posts" className="input input-bordered w-full" />
</label>
</div>
<div className="flex justify-end gap-2 pt-4">
<button type="button" className="btn btn-ghost btn-sm" onClick={() => setShowModal(false)}>Cancel</button>
<button type="submit" className="btn btn-primary btn-sm" disabled={creating}>
{creating ? <span className="loading loading-spinner loading-sm" /> : 'Create Collection'}
</button>
</div>
</form>
</div>
<div className="modal-backdrop" onClick={() => setShowModal(false)} />
</dialog>
)}
</div>
);
}