UCDTBEK3CF6YT2H6V57HI6FAFW44BIYYAK3Z2QJ5LJE7QWX7OEYAC details: error instanceof Error ? error.message : String(error),},500);}});/*** Protected API route to update an existing dataset** @route PUT /api/datasets/:id* @authentication Required* @param {string} id - Dataset ID in URL path* @body {Object} Dataset update payload:* - name?: string (optional, max 255 chars)* - description?: string (optional, max 255 chars)* - public?: boolean (optional)* - type?: string (optional)* - active?: boolean (optional, for soft delete)* @returns {Object} Response containing:* - data: The updated dataset object* @error 400 - If fields are invalid or dataset not found* @error 403 - If user doesn't own the dataset* @error 500 - If database operation fails* @description Updates an existing dataset owned by the authenticated user* Only the dataset owner can modify it*/app.put("/api/datasets/:id", 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// Get dataset ID from URL parametersconst datasetId = c.req.param("id");if (!datasetId) {return c.json({error: "Missing dataset ID in URL"}, 400);}// Parse request bodyconst body = await c.req.json();const { name, description, public: isPublic, type, active } = body;// Connect to the databaseconst sql = neon(c.env.DATABASE_URL);const db = drizzle(sql);// First, check if the dataset exists and if the user owns itconst existingDataset = await db.select({id: dataset.id,owner: dataset.owner,active: dataset.active}).from(dataset).where(eq(dataset.id, datasetId)).limit(1);if (existingDataset.length === 0) {return c.json({error: "Dataset not found"}, 404);}// Check ownershipif (existingDataset[0].owner !== userId) {return c.json({error: "You don't have permission to modify this dataset"}, 403);}// Validate fields if providedif (name !== undefined) {if (typeof name !== 'string' || name.trim().length === 0) {return c.json({error: "Invalid field: name must be a non-empty string"}, 400);}if (name.length > 255) {return c.json({error: "Field 'name' must be 255 characters or less"}, 400);}}if (description !== undefined && description !== null && description.length > 255) {return c.json({error: "Field 'description' must be 255 characters or less"}, 400);}if (type !== undefined) {const validTypes = ['organise', 'test', 'train'];if (!validTypes.includes(type)) {return c.json({error: `Field 'type' must be one of: ${validTypes.join(', ')}`}, 400);}}// Build update object with only provided fieldsconst updateData: Record<string, unknown> = {lastModified: new Date(),modifiedBy: userId,};if (name !== undefined) {updateData.name = name.trim();}if (description !== undefined) {updateData.description = description?.trim() || null;}if (isPublic !== undefined) {updateData.public = Boolean(isPublic);}if (type !== undefined) {updateData.type = type;}if (active !== undefined) {updateData.active = Boolean(active);}// Update the datasetconst result = await db.update(dataset).set(updateData).where(eq(dataset.id, datasetId)).returning({id: dataset.id,name: dataset.name,description: dataset.description,public: dataset.public,type: dataset.type,createdAt: dataset.createdAt,lastModified: dataset.lastModified,owner: dataset.owner,active: dataset.active,});if (result.length === 0) {return c.json({error: "Failed to update dataset"}, 500);}console.log("Updated dataset:", result[0].id, "for user:", userId);return c.json({data: result[0]});} catch (error) {console.error("Error updating dataset:", error);return c.json({error: "Failed to update dataset",
const [showCreateForm, setShowCreateForm] = useState<boolean>(false);const [createLoading, setCreateLoading] = useState<boolean>(false);const [createError, setCreateError] = useState<string | null>(null);
const [showForm, setShowForm] = useState<boolean>(false);const [formLoading, setFormLoading] = useState<boolean>(false);const [formError, setFormError] = useState<string | null>(null);const [editingDataset, setEditingDataset] = useState<Dataset | null>(null);const [isEditMode, setIsEditMode] = useState<boolean>(false);
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 (isEditMode && editingDataset) {// Update existing datasetconst response = await fetch(`/api/datasets/${editingDataset.id}`, {method: "PUT",headers: {"Authorization": `Bearer ${accessToken}`,"Content-Type": "application/json",},body: JSON.stringify({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: updatedDataset } = await response.json();// Update the dataset in the listsetDatasets(prev => prev.map(dataset =>dataset.id === updatedDataset.id ? updatedDataset : dataset));} else {// Create new datasetconst 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}`);}
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]);
setCreateError(err instanceof Error ? err.message : "Failed to create dataset");console.error("Error creating dataset:", err);
setFormError(err instanceof Error ? err.message : `Failed to ${isEditMode ? 'update' : 'create'} dataset`);console.error(`Error ${isEditMode ? 'updating' : 'creating'} dataset:`, err);
const handleCancelCreate = () => {setShowCreateForm(false);setCreateError(null);
const handleCancelForm = () => {setShowForm(false);setFormError(null);setIsEditMode(false);setEditingDataset(null);setFormData({name: '',description: '',type: 'organise',public: false});};const handleCreateNew = () => {setIsEditMode(false);setEditingDataset(null);
const handleEditDataset = (dataset: Dataset) => {setIsEditMode(true);setEditingDataset(dataset);setFormData({name: dataset.name,description: dataset.description || '',type: dataset.type as 'organise' | 'test' | 'train',public: dataset.public});setShowForm(true);};const handleDeleteDataset = async (dataset: Dataset) => {if (!isAuthenticated) return;const confirmDelete = window.confirm(`Are you sure you want to delete "${dataset.name}"? This action cannot be undone.`);if (!confirmDelete) return;try {const accessToken = await getAccessToken();const response = await fetch(`/api/datasets/${dataset.id}`, {method: "PUT",headers: {"Authorization": `Bearer ${accessToken}`,"Content-Type": "application/json",},body: JSON.stringify({active: false}),});if (!response.ok) {const errorData = await response.json();throw new Error(errorData.error || `HTTP error! Status: ${response.status}`);}// Remove the dataset from the list (soft delete)setDatasets(prev => prev.filter(d => d.id !== dataset.id));} catch (err) {setError(err instanceof Error ? err.message : "Failed to delete dataset");console.error("Error deleting dataset:", err);}};
<TableCell className="font-medium whitespace-normal break-words">{dataset.name}</TableCell><TableCell className="text-center">
<TableCellclassName="font-medium whitespace-normal break-words cursor-pointer"onClick={() => onDatasetSelect(dataset.id, dataset.name)}>{dataset.name}</TableCell><TableCellclassName="text-center cursor-pointer"onClick={() => onDatasetSelect(dataset.id, dataset.name)}>
<TableCell className="whitespace-normal break-words">{dataset.description || "—"}</TableCell>
<TableCell className="text-center"><div className="flex justify-center gap-2"><ButtononClick={(e) => {e.stopPropagation();handleEditDataset(dataset);}}variant="ghost"size="sm"className="p-1 h-8 w-8"title="Edit dataset"><Edit className="h-4 w-4" /></Button><ButtononClick={(e) => {e.stopPropagation();handleDeleteDataset(dataset);}}variant="ghost"size="sm"className="p-1 h-8 w-8 text-red-600 hover:text-red-700 hover:bg-red-50"title="Delete dataset"><Trash2 className="h-4 w-4" /></Button></div></TableCell>