From 52f29fa1018d4866773c748742f1891d8052599f Mon Sep 17 00:00:00 2001 From: paul-nothaft <40865108-paul-nothaft@users.noreply.replit.com> Date: Thu, 11 Sep 2025 11:54:28 +0000 Subject: [PATCH] Add a system for organizing tasks with customizable color-coded labels Implement a label system with API endpoints for CRUD operations on labels and tasks, including schema definitions for labels and tasks with label associations. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ceced2fc-aa46-458d-ba87-ddd4b7bb1518 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/659922a9-0087-461c-90dd-6d9a58b81d4d/ceced2fc-aa46-458d-ba87-ddd4b7bb1518/XrG8SoT --- .replit | 4 -- server/routes.ts | 133 ++++++++++++++++++++++++++++++++++++++++++++-- server/storage.ts | 106 +++++++++++++++++++++++++++++++++++- shared/schema.ts | 34 +++++++++++- 4 files changed, 267 insertions(+), 10 deletions(-) diff --git a/.replit b/.replit index c1b533b..930416a 100644 --- a/.replit +++ b/.replit @@ -14,10 +14,6 @@ run = ["npm", "run", "start"] localPort = 5000 externalPort = 80 -[[ports]] -localPort = 39485 -externalPort = 3001 - [[ports]] localPort = 41353 externalPort = 3000 diff --git a/server/routes.ts b/server/routes.ts index 9da3a81..f93caac 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -1,13 +1,138 @@ import type { Express } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage"; +import { insertLabelSchema, insertTaskSchema } from "@shared/schema"; export async function registerRoutes(app: Express): Promise { - // put application routes here - // prefix all routes with /api + // Labels API routes + app.get("/api/labels", async (req, res) => { + try { + const labels = await storage.getAllLabels(); + res.json(labels); + } catch (error) { + res.status(500).json({ error: "Failed to fetch labels" }); + } + }); - // use storage to perform CRUD operations on the storage interface - // e.g. storage.insertUser(user) or storage.getUserByUsername(username) + app.get("/api/labels/:id", async (req, res) => { + try { + const label = await storage.getLabel(req.params.id); + if (!label) { + return res.status(404).json({ error: "Label not found" }); + } + res.json(label); + } catch (error) { + res.status(500).json({ error: "Failed to fetch label" }); + } + }); + + app.post("/api/labels", async (req, res) => { + try { + const result = insertLabelSchema.safeParse(req.body); + if (!result.success) { + return res.status(400).json({ error: "Invalid label data", details: result.error }); + } + + const label = await storage.createLabel(result.data); + res.status(201).json(label); + } catch (error) { + res.status(500).json({ error: "Failed to create label" }); + } + }); + + app.patch("/api/labels/:id", async (req, res) => { + try { + const updates = insertLabelSchema.partial().safeParse(req.body); + if (!updates.success) { + return res.status(400).json({ error: "Invalid label data", details: updates.error }); + } + + const label = await storage.updateLabel(req.params.id, updates.data); + if (!label) { + return res.status(404).json({ error: "Label not found" }); + } + res.json(label); + } catch (error) { + res.status(500).json({ error: "Failed to update label" }); + } + }); + + app.delete("/api/labels/:id", async (req, res) => { + try { + const success = await storage.deleteLabel(req.params.id); + if (!success) { + return res.status(404).json({ error: "Label not found" }); + } + res.status(204).send(); + } catch (error) { + res.status(500).json({ error: "Failed to delete label" }); + } + }); + + // Tasks API routes + app.get("/api/tasks", async (req, res) => { + try { + const tasks = await storage.getAllTasks(); + res.json(tasks); + } catch (error) { + res.status(500).json({ error: "Failed to fetch tasks" }); + } + }); + + app.get("/api/tasks/:id", async (req, res) => { + try { + const task = await storage.getTask(req.params.id); + if (!task) { + return res.status(404).json({ error: "Task not found" }); + } + res.json(task); + } catch (error) { + res.status(500).json({ error: "Failed to fetch task" }); + } + }); + + app.post("/api/tasks", async (req, res) => { + try { + const result = insertTaskSchema.safeParse(req.body); + if (!result.success) { + return res.status(400).json({ error: "Invalid task data", details: result.error }); + } + + const task = await storage.createTask(result.data); + res.status(201).json(task); + } catch (error) { + res.status(500).json({ error: "Failed to create task" }); + } + }); + + app.patch("/api/tasks/:id", async (req, res) => { + try { + const updates = insertTaskSchema.partial().safeParse(req.body); + if (!updates.success) { + return res.status(400).json({ error: "Invalid task data", details: updates.error }); + } + + const task = await storage.updateTask(req.params.id, updates.data); + if (!task) { + return res.status(404).json({ error: "Task not found" }); + } + res.json(task); + } catch (error) { + res.status(500).json({ error: "Failed to update task" }); + } + }); + + app.delete("/api/tasks/:id", async (req, res) => { + try { + const success = await storage.deleteTask(req.params.id); + if (!success) { + return res.status(404).json({ error: "Task not found" }); + } + res.status(204).send(); + } catch (error) { + res.status(500).json({ error: "Failed to delete task" }); + } + }); const httpServer = createServer(app); diff --git a/server/storage.ts b/server/storage.ts index ee25bd1..ca95dbc 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -1,4 +1,4 @@ -import { type User, type InsertUser } from "@shared/schema"; +import { type User, type InsertUser, type Label, type InsertLabel, type Task, type InsertTask } from "@shared/schema"; import { randomUUID } from "crypto"; // modify the interface with any CRUD methods @@ -8,13 +8,47 @@ export interface IStorage { getUser(id: string): Promise; getUserByUsername(username: string): Promise; createUser(user: InsertUser): Promise; + + // Labels + getAllLabels(): Promise; + getLabel(id: string): Promise