JHFIJJSLVMQNYIDE6CXWUK5UTB7BTUSY7NCI33WKCWIRZHCSMBHQC * Protected API route to create a new dataset** @route POST /api/datasets* @authentication Required* @body {Object} Dataset creation payload:* - id: string (nanoid(12) - user generated)* - name: string (required, max 255 chars)* - description?: string (optional, max 255 chars)* - public?: boolean (optional, defaults to false)* - type?: string (optional, defaults to 'organise')* @returns {Object} Response containing:* - data: The created dataset object* @error 400 - If required fields are missing or invalid* @error 500 - If database operation fails* @description Creates a new dataset for the authenticated user* The user becomes the owner, creator, and modifier of the dataset*/app.post("/api/datasets", authenticate, async (c) => {try {// Get the JWT payload (user info)const jwtPayload = (c as unknown as { jwtPayload: JWTPayload }).jwtPayload;const userId = jwtPayload.sub; // User ID from JWT// Parse request bodyconst body = await c.req.json();const { id, name, description, public: isPublic, type } = body;// Validate required fieldsif (!id || typeof id !== 'string') {return c.json({error: "Missing or invalid required field: id"}, 400);}if (!name || typeof name !== 'string' || name.trim().length === 0) {return c.json({error: "Missing or invalid required field: name"}, 400);}// Validate field lengthsif (id.length !== 12) {return c.json({error: "Field 'id' must be exactly 12 characters (nanoid)"}, 400);}if (name.length > 255) {return c.json({error: "Field 'name' must be 255 characters or less"}, 400);}if (description && description.length > 255) {return c.json({error: "Field 'description' must be 255 characters or less"}, 400);}// Validate type if providedconst validTypes = ['organise', 'test', 'train'];const datasetType = type || 'organise';if (!validTypes.includes(datasetType)) {return c.json({error: `Field 'type' must be one of: ${validTypes.join(', ')}`}, 400);}// Connect to the databaseconst sql = neon(c.env.DATABASE_URL);const db = drizzle(sql);// Create the datasetconst now = new Date();const newDataset = {id: id.trim(),name: name.trim(),description: description?.trim() || null,public: Boolean(isPublic),type: datasetType,createdBy: userId,createdAt: now,lastModified: now,modifiedBy: userId,owner: userId,active: true,};// Insert the datasetconst result = await db.insert(dataset).values(newDataset).returning({id: dataset.id,name: dataset.name,description: dataset.description,public: dataset.public,type: dataset.type,createdAt: dataset.createdAt,owner: dataset.owner,});console.log("Created dataset:", result[0].id, "for user:", userId);return c.json({data: result[0]}, 201);} catch (error) {console.error("Error creating dataset:", error);// Handle unique constraint violationsif (error instanceof Error && error.message.includes('duplicate key')) {return c.json({error: "A dataset with this ID already exists"}, 400);}return c.json({error: "Failed to create dataset",details: error instanceof Error ? error.message : String(error),},500);}});/**
const [showCreateForm, setShowCreateForm] = useState<boolean>(false);const [createLoading, setCreateLoading] = useState<boolean>(false);const [createError, setCreateError] = useState<string | null>(null);// Form stateconst [formData, setFormData] = useState({name: '',description: '',type: 'organise' as 'organise' | 'test' | 'train',public: false});
const handleCreateDataset = async (e: React.FormEvent) => {e.preventDefault();if (!isAuthenticated) return;setCreateLoading(true);setCreateError(null);try {const accessToken = await getAccessToken();const id = nanoid(12);const response = await fetch("/api/datasets", {method: "POST",headers: {"Authorization": `Bearer ${accessToken}`,"Content-Type": "application/json",},body: JSON.stringify({id,name: formData.name,description: formData.description || undefined,type: formData.type,public: formData.public,}),});if (!response.ok) {const errorData = await response.json();throw new Error(errorData.error || `HTTP error! Status: ${response.status}`);}const { data: newDataset } = await response.json();// Add the new dataset to the listsetDatasets(prev => [newDataset, ...prev]);// Reset form and close modalsetFormData({name: '',description: '',type: 'organise',public: false});setShowCreateForm(false);} catch (err) {setCreateError(err instanceof Error ? err.message : "Failed to create dataset");console.error("Error creating dataset:", err);} finally {setCreateLoading(false);}};const handleFormChange = (field: keyof typeof formData, value: string | boolean) => {setFormData(prev => ({...prev,[field]: value}));};const handleCancelCreate = () => {setShowCreateForm(false);setCreateError(null);setFormData({name: '',description: '',type: 'organise',public: false});};
{/* Header with Add Button */}{isAuthenticated && !authLoading && (<div className="flex justify-between items-center mb-4"><h2 className="text-lg font-semibold">Datasets</h2><ButtononClick={() => setShowCreateForm(true)}variant="default"size="sm"className="flex items-center gap-2"><Plus className="h-4 w-4" />Add Dataset</Button></div>)}
{/* Create Dataset Modal */}{showCreateForm && (<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"><div className="bg-white rounded-lg p-6 w-full max-w-md mx-4"><div className="flex justify-between items-center mb-4"><h3 className="text-lg font-semibold">Create New Dataset</h3><ButtononClick={handleCancelCreate}variant="ghost"size="sm"className="p-1"><X className="h-4 w-4" /></Button></div>{createError && (<div className="bg-red-50 border border-red-200 text-red-700 px-3 py-2 rounded mb-4">{createError}</div>)}<form onSubmit={handleCreateDataset} className="space-y-4"><div><label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">Name *</label><inputtype="text"id="name"requiredmaxLength={255}value={formData.name}onChange={(e) => handleFormChange('name', e.target.value)}className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"placeholder="Enter dataset name"/></div><div><label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-1">Description</label><textareaid="description"maxLength={255}value={formData.description}onChange={(e) => handleFormChange('description', e.target.value)}className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"placeholder="Enter description (optional)"rows={3}/></div><div><label htmlFor="type" className="block text-sm font-medium text-gray-700 mb-1">Type</label><selectid="type"value={formData.type}onChange={(e) => handleFormChange('type', e.target.value as 'organise' | 'test' | 'train')}className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"><option value="organise">Organise</option><option value="test">Test</option><option value="train">Train</option></select></div><div className="flex items-center"><inputtype="checkbox"id="public"checked={formData.public}onChange={(e) => handleFormChange('public', e.target.checked)}className="mr-2"/><label htmlFor="public" className="text-sm font-medium text-gray-700">Make this dataset public</label></div><div className="flex justify-end space-x-3 pt-4"><Buttontype="button"onClick={handleCancelCreate}variant="outline"disabled={createLoading}>Cancel</Button><Buttontype="submit"disabled={createLoading || !formData.name.trim()}>{createLoading ? "Creating..." : "Create Dataset"}</Button></div></form></div></div>)}