diff --git a/backend/bun.lock b/backend/bun.lock index 0b8ada6..b4ee8dc 100644 --- a/backend/bun.lock +++ b/backend/bun.lock @@ -9,6 +9,7 @@ "dotenv": "^16.5.0", "drizzle-orm": "^0.44.2", "express": "^5.1.0", + "socket.io": "^4.8.1", }, "devDependencies": { "drizzle-kit": "^0.31.1", @@ -103,10 +104,14 @@ "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], + "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], + "@types/body-parser": ["@types/body-parser@1.19.5", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg=="], "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + "@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="], + "@types/express": ["@types/express@5.0.1", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "*" } }, "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ=="], "@types/express-serve-static-core": ["@types/express-serve-static-core@5.0.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA=="], @@ -129,6 +134,8 @@ "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="], + "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -169,6 +176,10 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "engine.io": ["engine.io@6.6.4", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g=="], + + "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -287,6 +298,12 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "socket.io": ["socket.io@4.8.1", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="], + + "socket.io-adapter": ["socket.io-adapter@2.5.5", "", { "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" } }, "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg=="], + + "socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], @@ -309,10 +326,24 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + "@libsql/isomorphic-ws/ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + + "engine.io/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "engine.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -356,5 +387,17 @@ "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "engine.io/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "engine.io/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "socket.io/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "socket.io/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "engine.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "socket.io/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], } } diff --git a/backend/package.json b/backend/package.json index a3ec262..3423c01 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,8 @@ "cors": "^2.8.5", "dotenv": "^16.5.0", "drizzle-orm": "^0.44.2", - "express": "^5.1.0" + "express": "^5.1.0", + "socket.io": "^4.8.1" }, "scripts": { "dev": "bun run --watch src/main.ts" diff --git a/backend/src/db/schema.ts b/backend/src/db/schema.ts index 9893c07..30129df 100644 --- a/backend/src/db/schema.ts +++ b/backend/src/db/schema.ts @@ -2,7 +2,7 @@ import { sqliteTable, int, text } from 'drizzle-orm/sqlite-core' export const event = sqliteTable('event', { id: int().primaryKey({ autoIncrement: true }), - userid: text().notNull(), + userid: text().notNull(), title: text().notNull(), description: text().notNull(), from: text().notNull(), @@ -12,13 +12,14 @@ export const event = sqliteTable('event', { }) export const task = sqliteTable('task', { - id: int().primaryKey({ autoIncrement: true }), - userid: text().notNull(), + id: int().primaryKey({ autoIncrement: true }), + userid: text().notNull(), title: text().notNull(), description: text().notNull(), - done: int().notNull(), - estimated_time: int().notNull(), - due_date: text().notNull(), + done: int().notNull(), + scheduled_at: text(), + estimated_time: int().notNull(), + due_date: text(), created_at: text().notNull().default(new Date().toISOString()), updated_at: text().notNull().default(new Date().toISOString()) }) diff --git a/backend/src/main.ts b/backend/src/main.ts index 5ca06e2..a354a4e 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,141 +1,41 @@ import express from 'express' import cors from 'cors' -import { drizzle } from 'drizzle-orm/libsql'; -import { event, task } from './db/schema'; -import { eq, ne, gt, gte } from 'drizzle-orm'; +import http from 'http' +import taskRouter from './routers/task'; +import eventRouter from './routers/event' + +import { Server} from 'socket.io' -const db = drizzle("file:local.db"); const app = express(); -const userId = "Detlef"; - -type Prettify = { - [K in keyof T]: T[K]; -} & {}; - -type TaskResponse = Prettify & { done: boolean }> +const server = http.createServer(app) +const io = new Server (server, { + cors:{ + origin: "*", + }, +}) app.use(cors()) +app.use('/tasks', taskRouter) +app.use('/events', eventRouter) app.use(express.json()); app.get('/', (req, res) => { - res.send('Hello World'); + res.send('Hello World'); }); -app.get('/tasks', async (req, res) => { - const tasks: typeof task.$inferSelect[] = await db.select().from(task) - res.status(200).send(tasks.map(task => { - return { ...task, done: task.done === 1 } - })); +io.on('connection', (socket) => { + console.log('A user connected:', socket.id); + + socket.on('change', () => { + console.log('Message received'); + socket.broadcast.emit('change') + }); + + socket.on('disconnect', () => { + console.log('User disconnected:', socket.id); + }); }); -app.get('/events', async(req, res) => { - res.status(200).send(await db.select().from(event)) -}); - -app.get('/user/:id', (req, res) => { - const id = req.params['id']; - - if (id == null) { - res.status(400).send({error: 'Needs an user id'}); - return; - } - - const user = {id: id, name: 'Cracker'} //TODO - res.json(user); - -}); - -app.get('/task/:id', async(req, res) => { - - const id = parseInt(req.params['id']); - - if (id == null) { - res.status(400).send({error: 'Needs an id'}); - return; - } - - const returnedTask = await db.select().from(task).where(eq(task.id, id)) - // - console.log(returnedTask) - res.json(returnedTask); -}); - -app.get('/event/:id', (req, res) => { - - const id = req.params['id']; - - if (id == null) { - res.status(400).send({error: 'Needs an id'}); - return; - } - - const event = {id: id, name: 'Pary'} //TODO - res.json(event); -}); - -app.post('/task', async(req, res) => { - - const newTask = req.body - newTask.userid = userId - - const returnedTask = await db.insert(task).values(newTask).returning() - console.log(returnedTask) - - res.status(201).json(returnedTask); -}); - -app.post('/event', async(req, res) => { - - const newEvent: typeof event.$inferInsert = req.body - newEvent.userid = userId - - const returnedEvent = await db.insert(event).values(newEvent).returning() - console.log(returnedEvent) - - res.status(201).json(returnedEvent); -}); - -app.put('/task', (req, res) => { - - const id = parseInt(req.params['id']); - const updatedTask: Partial = req.body - - if (id == null) { - res.status(400).send({error: 'Needs an id'}); - return; - } - db.update(task).set(updatedTask).where(eq(task.id, id)) - - res.status(200).json(updatedTask); -}); - -app.put('/event', (req, res) => { - - const id = parseInt(req.params['id']); - const updatedEvent: Partial = req.body - - if (id == null) { - res.status(400).send({error: 'Needs an id'}); - return; - } - db.update(event).set(updatedEvent).where(eq(event.id, id)) - - res.status(200).json(updatedEvent);}); - -app.delete('/task/:id', async(req, res) => { - const id = parseInt(req.params['id']); - - const success = await db.delete(task).where(eq(task.id, id)) - res.send("Deleted"); -}); - -app.delete('/event/:id', async(req, res) => { - const id = parseInt(req.params['id']); - - const success = await db.delete(event).where(eq(event.id, id)) - res.send("Deleted"); -}); - -app.listen(8080, () => { - console.log('Listening on port 8080'); +server.listen(8080, () => { + console.log('Listening on port 8080'); }); diff --git a/backend/src/routers/event.ts b/backend/src/routers/event.ts new file mode 100644 index 0000000..6e2c3a5 --- /dev/null +++ b/backend/src/routers/event.ts @@ -0,0 +1,65 @@ +import express from 'express' +import cors from 'cors' +import { drizzle } from 'drizzle-orm/libsql'; +import { event } from '../db/schema'; +import { eq, ne, gt, gte } from 'drizzle-orm'; +import { Router } from "express"; + +const db = drizzle("file:local.db"); +const userId = "Detlef"; + +const router = Router() + +router.use(cors()) +router.use(express.json()); + +router.get('/', async (req, res) => { + res.status(200).send(await db.select().from(event)) +}); + +router.get('/:id', (req, res) => { + + const id = req.params['id']; + + if (id == null) { + res.status(400).send({ error: 'Needs an id' }); + return; + } + + const event = { id: id, name: 'Pary' } //TODO + res.json(event); +}); + +router.post('/', async (req, res) => { + console.log("loll") + const newEvent: typeof event.$inferInsert = req.body + newEvent.userid = userId + + const returnedEvent = await db.insert(event).values(newEvent).returning() + console.log(returnedEvent) + + res.status(201).json(returnedEvent); +}); + +router.put('/:id', async (req, res) => { + + const id = parseInt(req.params['id']); + const updatedEvent: Partial = req.body + + if (id == null) { + res.status(400).send({ error: 'Needs an id' }); + return; + } + await db.update(event).set(updatedEvent).where(eq(event.id, id)) + + res.status(200).json(updatedEvent); +}); + +router.delete('/:id', async (req, res) => { + const id = parseInt(req.params['id']); + + const success = await db.delete(event).where(eq(event.id, id)) + res.send("Deleted"); +}); + +export default router \ No newline at end of file diff --git a/backend/src/routers/task.ts b/backend/src/routers/task.ts new file mode 100644 index 0000000..b7b8ab1 --- /dev/null +++ b/backend/src/routers/task.ts @@ -0,0 +1,78 @@ +import express from 'express' +import cors from 'cors' +import { drizzle } from 'drizzle-orm/libsql'; +import { task } from '../db/schema'; +import { eq, ne, gt, gte } from 'drizzle-orm'; +import { Router } from "express"; + +const db = drizzle("file:local.db"); +const userId = "Detlef"; + +type Prettify = { + [K in keyof T]: T[K]; +} & {}; + +type TaskResponse = Prettify & { done: boolean }> + +const router = Router() + +router.use(cors()) +router.use(express.json()); + +router.get('/', async (req, res) => { + const tasks: typeof task.$inferSelect[] = await db.select().from(task) + console.log(tasks) + res.status(200).send(tasks.map(task => { + return { ...task, done: task.done === 1 } + })); +}); + +router.get('/:id', async (req, res) => { + + const id = parseInt(req.params['id']); + + if (id == null) { + res.status(400).send({ error: 'Needs an id' }); + return; + } + + const returnedTask = await db.select().from(task).where(eq(task.id, id)) + // + console.log(returnedTask) + res.json(returnedTask); +}); + +router.post('/', async (req, res) => { + + const newTask = req.body + newTask.userid = userId + + console.log(newTask) + const returnedTasks = await db.insert(task).values(newTask).returning() + console.log(returnedTasks) + + res.status(201).json(returnedTasks[0]); +}); + +router.put('/:id', async (req, res) => { + + const id = parseInt(req.params['id']); + const updatedTask: Partial = req.body + + if (id == null) { + res.status(400).send({ error: 'Needs an id' }); + return; + } + await db.update(task).set(updatedTask).where(eq(task.id, id)) + + res.status(200).json(updatedTask); +}); + +router.delete('/:id', async (req, res) => { + const id = parseInt(req.params['id']); + + const success = await db.delete(task).where(eq(task.id, id)) + res.send("Deleted"); +}); + +export default router \ No newline at end of file diff --git a/web/bun.lock b/web/bun.lock index 27559b5..9777b8d 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -18,6 +18,7 @@ "moment": "^2.30.1", "nuxt": "^3.17.2", "nuxt-app": "file:", + "socket.io-client": "^4.8.1", "typescript": "^5.6.3", "vue": "^3.5.13", "vue-router": "^4.5.1", @@ -422,6 +423,8 @@ "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], + "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], + "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="], "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], @@ -940,6 +943,10 @@ "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="], + "engine.io-client": ["engine.io-client@6.6.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w=="], + + "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -1778,6 +1785,10 @@ "smob": ["smob@1.5.0", "", {}, "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig=="], + "socket.io-client": ["socket.io-client@4.8.1", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ=="], + + "socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="], + "source-map": ["source-map@0.7.4", "", {}, "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -2026,6 +2037,8 @@ "xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="], + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], @@ -2192,6 +2205,10 @@ "dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "eslint/find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], @@ -2294,6 +2311,10 @@ "send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "socket.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "spdx-correct/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], diff --git a/web/components/ui/MainContent.vue b/web/components/ui/MainContent.vue index 2c8bf5f..f10add7 100644 --- a/web/components/ui/MainContent.vue +++ b/web/components/ui/MainContent.vue @@ -6,16 +6,23 @@ import type { DateTime } from 'luxon'; const events = defineModel('events', { required: true }) const date = defineModel('date', { required: true }) +const draggedTask = defineModel('draggedTask', { required: true }) +const tasks = defineModel('tasks', { required: true }) const emits = defineEmits<{ (e: 'createEvent', event: Event): void + (e: 'edit-task', task: Task): void + (e: 'edit-event', event: Event): void + (e: 'delete-event', id: number): void }>() diff --git a/web/components/ui/Sidebar.vue b/web/components/ui/Sidebar.vue index dd3a118..d337a79 100644 --- a/web/components/ui/Sidebar.vue +++ b/web/components/ui/Sidebar.vue @@ -4,19 +4,24 @@ import ListItem from './ListItem.vue'; import Title1 from './Title1.vue'; import type { DropdownMenuItem } from '@nuxt/ui'; import { DateTime } from 'luxon'; -import type { USeparator } from '#components'; const colorMode = useColorMode(); +const toast = useToast() +const instance = getCurrentInstance() const currentTheme = ref<'dark' | 'system' | 'light'>(colorMode.preference as 'dark' | 'system' | 'light'); +const showTaskCreateModal = ref(false); +const showTaskEditModal = ref(false); +const taskFormModalInput = ref>({}); const date = defineModel('date', { required: true }) const tasks = defineModel('tasks', { required: true }) const emits = defineEmits<{ - (e: 'createTask', name: string): void + (e: 'createTask', task: Task): void (e: 'deleteTask', id: number): void (e: 'editTask', task: Task): void + (e: 'scheduleTask', task: Task): void }>() const isLight = computed(() => currentTheme.value === 'light'); @@ -76,37 +81,51 @@ const selectedDate = computed({ } }) -type Task = { - id: number - userid: string - title: string - description: string - done: boolean - estimated_time: string - due_date: string - created_at: string - updated_at: string +function addTask(task: Task) { + tasks.value.push(task) + console.log(tasks.value) + emits('createTask', task) } - - -function addTask() { - const name = prompt("Todo name:") - console.log(name) - if (name !== null) { - emits('createTask', name) +function deleteTask(task: Task) { + if (task.id === undefined) { + toast.add({ + title: "Task does not exist anymore" + }) + return } + + tasks.value = tasks.value.filter(t => t.id !== task.id) + + emits('deleteTask', task.id) } -function deleteTask(todo: Task) { - emits('deleteTask', todo.id) -} + function editTask(task: Task) { emits('editTask', task) } +function openTaskFormModal(task: Partial) { + taskFormModalInput.value = task + showTaskCreateModal.value = true +} + +function openTaskEditModal(task: Task) { + taskFormModalInput.value = task + showTaskEditModal.value = true +} + +function scheduleTask(task: Task) { + emits('scheduleTask', task) +} +