From 2ba0d1618675263a0aedc5c4b40ef887591de3c6 Mon Sep 17 00:00:00 2001 From: AbdulPIO Date: Mon, 2 Jun 2025 17:29:20 +0530 Subject: [PATCH] TaskPlanner Initial Commit --- angular.json | 95 ++ db.json | 191 ++-- package-lock.json | 868 +++++++++++++----- package.json | 2 +- .../public/favicon.ico | Bin 0 -> 15086 bytes .../src/app/app.component.html | 3 + .../src/app/app.component.scss | 7 + .../src/app/app.component.spec.ts | 29 + .../src/app/app.component.ts | 15 + .../src/app/app.config.ts | 12 + .../src/app/app.routes.ts | 7 + .../project-card/project-card.component.html | 53 ++ .../project-card/project-card.component.scss | 72 ++ .../project-card/project-card.component.ts | 111 +++ .../project-form-modal.component.html | 36 + .../project-form-modal.component.scss | 93 ++ .../project-form-modal.component.ts | 63 ++ .../project-list/project-list.component.html | 25 + .../project-list/project-list.component.scss | 49 + .../project-list/project-list.component.ts | 78 ++ .../task-card/task-card.component.html | 43 + .../task-card/task-card.component.scss | 96 ++ .../task-card/task-card.component.ts | 24 + .../task-form-modal.component.html | 64 ++ .../task-form-modal.component.scss | 93 ++ .../task-form-modal.component.ts | 85 ++ .../overdue-highlighter.directive.ts | 29 + .../src/app/models/task.interface.ts | 14 + .../src/app/pipes/task-status-filter.pipe.ts | 16 + .../src/app/services/task.service.ts | 51 + .../taskplanner-angular-app/src/index.html | 17 + projects/taskplanner-angular-app/src/main.ts | 6 + .../taskplanner-angular-app/src/styles.scss | 16 + .../taskplanner-angular-app/tsconfig.app.json | 18 + .../tsconfig.spec.json | 15 + tsconfig.json | 2 +- 36 files changed, 2115 insertions(+), 283 deletions(-) create mode 100644 projects/taskplanner-angular-app/public/favicon.ico create mode 100644 projects/taskplanner-angular-app/src/app/app.component.html create mode 100644 projects/taskplanner-angular-app/src/app/app.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/app.component.spec.ts create mode 100644 projects/taskplanner-angular-app/src/app/app.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/app.config.ts create mode 100644 projects/taskplanner-angular-app/src/app/app.routes.ts create mode 100644 projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.html create mode 100644 projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.html create mode 100644 projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.html create mode 100644 projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.html create mode 100644 projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.html create mode 100644 projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.scss create mode 100644 projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.ts create mode 100644 projects/taskplanner-angular-app/src/app/directives/overdue-highlighter.directive.ts create mode 100644 projects/taskplanner-angular-app/src/app/models/task.interface.ts create mode 100644 projects/taskplanner-angular-app/src/app/pipes/task-status-filter.pipe.ts create mode 100644 projects/taskplanner-angular-app/src/app/services/task.service.ts create mode 100644 projects/taskplanner-angular-app/src/index.html create mode 100644 projects/taskplanner-angular-app/src/main.ts create mode 100644 projects/taskplanner-angular-app/src/styles.scss create mode 100644 projects/taskplanner-angular-app/tsconfig.app.json create mode 100644 projects/taskplanner-angular-app/tsconfig.spec.json diff --git a/angular.json b/angular.json index 1f8b068..6804e30 100644 --- a/angular.json +++ b/angular.json @@ -97,6 +97,101 @@ } } } + }, + "taskplanner-angular-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "projects/taskplanner-angular-app", + "sourceRoot": "projects/taskplanner-angular-app/src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/taskplanner-angular-app", + "index": "projects/taskplanner-angular-app/src/index.html", + "browser": "projects/taskplanner-angular-app/src/main.ts", + "polyfills": [ + "zone.js" + ], + "tsConfig": "projects/taskplanner-angular-app/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "projects/taskplanner-angular-app/public" + } + ], + "styles": [ + "projects/taskplanner-angular-app/src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "taskplanner-angular-app:build:production" + }, + "development": { + "buildTarget": "taskplanner-angular-app:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "projects/taskplanner-angular-app/tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "projects/taskplanner-angular-app/public" + } + ], + "styles": [ + "projects/taskplanner-angular-app/src/styles.scss" + ], + "scripts": [] + } + } + } } } } diff --git a/db.json b/db.json index 7dd6d70..aeaae93 100644 --- a/db.json +++ b/db.json @@ -1,75 +1,124 @@ { - "exams": [ - { - "id": "1744693071906", - "examName": "Economics", - "examKey": "Eco1234", - "phone": "876-345-8762", - "examHours": 2, - "examDetails": [ - { - "subject": "basics", - "marks": 5, - "no_of_questions": 5 - } - ] - }, - { - "id": "1744695093013", - "examName": "Hindi", - "examKey": "Hin123", - "phone": "967-397-3847", - "examHours": 3, - "examDetails": [] - }, - { - "id": "1744695131745", - "examName": "Math", - "examKey": "Math123", - "phone": "123-456-7890", - "examHours": 2, - "examDetails": [] - }, - { - "id": "1744696982851", - "examName": "German", - "examKey": "Ger123", - "phone": "897-455-3543", - "examHours": 2, - "examDetails": [] - }, - { - "id": "1744698285918", - "examName": "Reasoning", - "examKey": "Rea1234", - "phone": "834-234-8734", - "examHours": 3, - "examDetails": [ - { - "subject": "Aptitude", - "marks": 2, - "no_of_questions": 10 - }, - { - "subject": "Relation", - "marks": 3, - "no_of_questions": 10 - } - ] - }, - { - "id": "1747652050460", - "examName": "Sanskrit", - "examKey": "San1234", - "phone": "341-234-3242", - "examHours": 3, - "examDetails": [ - { - "subject": "Basics", - "marks": 10, - "no_of_questions": 2 - } - ] + "exams": [], + "projects": [ + { + "id": "91e7", + "projectTitle": "Website Redesign" + }, + { + "id": "b7c6", + "projectTitle": "Mobile App Launch" + }, + { + "id": "b0e2", + "projectTitle": "Marketing Campaign" + } + ], + "tasks": [ + { + "id": "ebd3", + "title": "Local Deploy", + "dueDate": "06/12/25", + "status": "Pending", + "projectId": null + }, + { + "id": "c585", + "title": "A post", + "dueDate": "05/28/25", + "status": "Pending", + "projectId": null + }, + { + "title": "Create Setup", + "dueDate": "05/29/25", + "status": "In Progress", + "id": "87a0", + "projectId": null + }, + { + "id": "b3b2", + "title": "Backend Development", + "dueDate": "06/10/25", + "status": "Pending", + "projectId": null + }, + { + "id": "d5d3", + "title": "Frontend Development", + "dueDate": "06/15/25", + "status": "Pending", + "projectId": null + }, + { + "id": "c236", + "title": "Client Meeting", + "dueDate": "06/05/25", + "status": "Pending", + "projectId": null + }, + { + "id": "920c", + "title": "Contract Finalizing", + "dueDate": "06/10/25", + "status": "Pending", + "projectId": null + }, + { + "id": "4b8b", + "title": "Contract Finalizing", + "dueDate": "06/10/25", + "status": "Pending", + "projectId": null + }, + { + "id": "2217", + "title": "Design new homepage", + "dueDate": "06/01/25", + "status": "Done", + "projectId": "91e7" + }, + { + "id": "957a", + "title": "Update Color Scheme", + "dueDate": "06/01/25", + "status": "In Progress", + "projectId": "91e7" + }, + { + "id": "4f9a", + "title": "Beta Testing", + "dueDate": "06/03/25", + "status": "Pending", + "projectId": "b7c6" + }, + { + "id": "6776", + "title": "Create social media posts", + "dueDate": "05/25/25", + "status": "Done", + "projectId": "b0e2" + }, + { + "id": "1140", + "title": "Email campaign setup", + "dueDate": "06/04/25", + "status": "In Progress", + "projectId": "b0e2" + }, + { + "id": "df4f", + "title": "Analyze campaign results", + "dueDate": "06/12/25", + "status": "Pending", + "projectId": "b0e2" + }, + { + "id": "d5b7", + "title": "Test", + "dueDate": "06/06/25", + "status": "In Progress", + "projectId": null } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 55e79e1..fb90a59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@angular/compiler-cli": "^18.0.0", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.1.0", + "json-server": "^1.0.0-beta.3", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.0", @@ -607,6 +608,36 @@ "semver": "bin/semver.js" } }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@angular/core": { "version": "18.2.13", "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.13.tgz", @@ -3760,6 +3791,13 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", @@ -4101,6 +4139,327 @@ "dev": true, "license": "MIT" }, + "node_modules/@tinyhttp/accepts": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.2.3.tgz", + "integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "4.0.4", + "negotiator": "^0.6.3" + }, + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/accepts/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/app": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-2.5.2.tgz", + "integrity": "sha512-DcB3Y8GQppLQlO2VxRYF7LzTEAoZb+VRQXuIsErcu2fNaM1xdx6NQZDso5rlZUiaeg6KYYRfU34N4XkZbv6jSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/proxy-addr": "2.2.1", + "@tinyhttp/req": "2.2.5", + "@tinyhttp/res": "2.2.5", + "@tinyhttp/router": "2.2.3", + "header-range-parser": "1.1.3", + "regexparam": "^2.0.2" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", + "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-type": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz", + "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4" + } + }, + "node_modules/@tinyhttp/cookie": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz", + "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/cookie-signature": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz", + "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/cors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz", + "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/vary": "^0.1.3" + }, + "engines": { + "node": ">=12.20 || 14.x || >=16" + } + }, + "node_modules/@tinyhttp/encode-url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz", + "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/etag": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz", + "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/forwarded": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.2.tgz", + "integrity": "sha512-9H/eulJ68ElY/+zYpTpNhZ7vxGV+cnwaR6+oQSm7bVgZMyuQfgROW/qvZuhmgDTIxnGMXst+Ba4ij6w6Krcs3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/logger": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.1.0.tgz", + "integrity": "sha512-Ma1fJ9CwUbn9r61/4HW6+nflsVoslpOnCrfQ6UeZq7GGIgwLzofms3HoSVG7M+AyRMJpxlfcDdbH5oFVroDMKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.20", + "dayjs": "^1.11.13", + "http-status-emojis": "^2.2.0" + }, + "engines": { + "node": ">=14.18 || >=16.20" + } + }, + "node_modules/@tinyhttp/proxy-addr": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-2.2.1.tgz", + "integrity": "sha512-BicqMqVI91hHq2BQmnqJUh0FQUnx7DncwSGgu2ghlh+JZG2rHK2ZN/rXkfhrx1rrUw6hnd0L36O8GPMh01+dDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/forwarded": "2.1.2", + "ipaddr.js": "^2.2.0" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/req": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.5.tgz", + "integrity": "sha512-trfsXwtmsNjMcGKcLJ+45h912kLRqBQCQD06ams3Tq0kf4gHLxjHjoYOC1Z9yGjOn81XllRx8wqvnvr+Kbe3gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/accepts": "2.2.3", + "@tinyhttp/type-is": "2.2.4", + "@tinyhttp/url": "2.1.1", + "header-range-parser": "^1.1.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/res": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.5.tgz", + "integrity": "sha512-yBsqjWygpuKAVz4moWlP4hqzwiDDqfrn2mA0wviJAcgvGiyOErtlQwXY7aj3aPiCpURvxvEFO//Gdy6yV+xEpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-disposition": "2.2.2", + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/cookie-signature": "2.1.1", + "@tinyhttp/encode-url": "2.1.1", + "@tinyhttp/req": "2.2.5", + "@tinyhttp/send": "2.2.3", + "@tinyhttp/vary": "^0.1.3", + "es-escape-html": "^0.1.1", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/res/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/router": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.3.tgz", + "integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.3.tgz", + "integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "@tinyhttp/etag": "2.1.2", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/type-is": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.4.tgz", + "integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/type-is/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz", + "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/vary": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz", + "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -5385,19 +5744,41 @@ "license": "MIT" }, "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/chownr": { @@ -6020,6 +6401,13 @@ "node": ">=4.0" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -6237,16 +6625,45 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { "node": ">= 0.4" @@ -6491,6 +6908,16 @@ "node": ">= 0.4" } }, + "node_modules/es-escape-html": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/es-escape-html/-/es-escape-html-0.1.1.tgz", + "integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.x" + } + }, "node_modules/es-module-lexer": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", @@ -6638,6 +7065,19 @@ "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -7450,6 +7890,16 @@ "node": ">= 0.4" } }, + "node_modules/header-range-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.3.tgz", + "integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, "node_modules/hosted-git-info": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", @@ -7655,6 +8105,13 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/http-status-emojis": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz", + "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==", + "dev": true, + "license": "MIT" + }, "node_modules/https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", @@ -7843,6 +8300,16 @@ "node": ">=8" } }, + "node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8373,6 +8840,77 @@ "dev": true, "license": "MIT" }, + "node_modules/json-server": { + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.3.tgz", + "integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==", + "dev": true, + "license": "SEE LICENSE IN ./LICENSE", + "dependencies": { + "@tinyhttp/app": "^2.4.0", + "@tinyhttp/cors": "^2.0.1", + "@tinyhttp/logger": "^2.0.0", + "chalk": "^5.3.0", + "chokidar": "^4.0.1", + "dot-prop": "^9.0.0", + "eta": "^3.5.0", + "inflection": "^3.0.0", + "json5": "^2.2.3", + "lowdb": "^7.0.1", + "milliparsec": "^4.0.0", + "sirv": "^2.0.4", + "sort-on": "^6.1.0" + }, + "bin": { + "json-server": "lib/bin.js" + }, + "engines": { + "node": ">=18.3" + } + }, + "node_modules/json-server/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/json-server/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/json-server/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -8552,31 +9090,6 @@ "source-map-support": "^0.5.5" } }, - "node_modules/karma/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/karma/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -8596,19 +9109,6 @@ "dev": true, "license": "MIT" }, - "node_modules/karma/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/karma/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -8619,32 +9119,6 @@ "node": ">=8" } }, - "node_modules/karma/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/karma/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/karma/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9202,6 +9676,22 @@ "node": ">=8.0" } }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "license": "MIT", + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -9366,6 +9856,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/milliparsec": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-4.0.0.tgz", + "integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -11027,17 +11527,29 @@ } }, "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">= 14.18.0" + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/reflect-metadata": { @@ -11091,6 +11603,16 @@ "dev": true, "license": "MIT" }, + "node_modules/regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/regexpu-core": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", @@ -11488,70 +12010,6 @@ } } }, - "node_modules/sass/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sass/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sass/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -11972,6 +12430,21 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -12166,6 +12639,22 @@ "node": ">= 14" } }, + "node_modules/sort-on": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-6.1.0.tgz", + "integrity": "sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -12339,6 +12828,19 @@ "node": ">= 0.6" } }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -12711,6 +13213,16 @@ "node": ">=0.6" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tree-dump": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", @@ -13770,31 +14282,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/webpack-dev-server/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -13816,19 +14303,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", @@ -13870,32 +14344,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/webpack-dev-server/node_modules/rimraf": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", diff --git a/package.json b/package.json index 2d88f96..9b47b23 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "watch": "ng build --watch --configuration development", "test": "ng test", "json-server": "json-server --watch db.json --port 3000" - }, "private": true, "dependencies": { @@ -30,6 +29,7 @@ "@angular/compiler-cli": "^18.0.0", "@types/jasmine": "~5.1.0", "jasmine-core": "~5.1.0", + "json-server": "^1.0.0-beta.3", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.0", diff --git a/projects/taskplanner-angular-app/public/favicon.ico b/projects/taskplanner-angular-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..57614f9c967596fad0a3989bec2b1deff33034f6 GIT binary patch literal 15086 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 Nr__JTFzvMr^FKp4S3v*( literal 0 HcmV?d00001 diff --git a/projects/taskplanner-angular-app/src/app/app.component.html b/projects/taskplanner-angular-app/src/app/app.component.html new file mode 100644 index 0000000..7c1ee00 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/projects/taskplanner-angular-app/src/app/app.component.scss b/projects/taskplanner-angular-app/src/app/app.component.scss new file mode 100644 index 0000000..7ae3d22 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.component.scss @@ -0,0 +1,7 @@ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; + min-height: 100vh; + background: #f5f5f5; +} diff --git a/projects/taskplanner-angular-app/src/app/app.component.spec.ts b/projects/taskplanner-angular-app/src/app/app.component.spec.ts new file mode 100644 index 0000000..33fd7b9 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.component.spec.ts @@ -0,0 +1,29 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have the 'taskplanner-angular-app' title`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('taskplanner-angular-app'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, taskplanner-angular-app'); + }); +}); diff --git a/projects/taskplanner-angular-app/src/app/app.component.ts b/projects/taskplanner-angular-app/src/app/app.component.ts new file mode 100644 index 0000000..895b3c0 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { ProjectListComponent } from './components/project-list/project-list.component'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [CommonModule, ProjectListComponent], + templateUrl: './app.component.html', + styleUrl: './app.component.scss' +}) +export class AppComponent { + title = 'taskplanner-angular-app'; +} diff --git a/projects/taskplanner-angular-app/src/app/app.config.ts b/projects/taskplanner-angular-app/src/app/app.config.ts new file mode 100644 index 0000000..766d1ea --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.config.ts @@ -0,0 +1,12 @@ +import { ApplicationConfig } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { routes } from './app.routes'; +import { provideHttpClient } from '@angular/common/http'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + provideHttpClient() + ] +}; diff --git a/projects/taskplanner-angular-app/src/app/app.routes.ts b/projects/taskplanner-angular-app/src/app/app.routes.ts new file mode 100644 index 0000000..aba30b9 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/app.routes.ts @@ -0,0 +1,7 @@ +import { Routes } from '@angular/router'; +import { ProjectListComponent } from './components/project-list/project-list.component'; + +export const routes: Routes = [ + { path: '', component: ProjectListComponent }, + { path: '**', redirectTo: '' } +]; diff --git a/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.html b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.html new file mode 100644 index 0000000..78a49ed --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.html @@ -0,0 +1,53 @@ +
+
+

{{ project.projectTitle }}

+
+ + + + +
+
+ +
+
+ +
+ +
+
+ {{ + tasks.length === 0 + ? "No tasks yet. Add a task to get started!" + : "No tasks match the selected filter." + }} +
+ +
+
+ + +
diff --git a/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.scss b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.scss new file mode 100644 index 0000000..3ee97de --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.scss @@ -0,0 +1,72 @@ +.project-card { + background: white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + overflow: hidden; + margin-bottom: 1.5rem; +} + +.project-header { + padding: 1rem; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; + display: flex; + justify-content: space-between; + align-items: center; +} + +h3 { + margin: 0; + color: #006064; +} + +.project-actions { + display: flex; + gap: 0.5rem; +} + +.btn-icon { + background: none; + border: none; + color: #666; + cursor: pointer; + padding: 0.5rem; + border-radius: 4px; + transition: all 0.2s; +} + +.btn-icon:hover { + background: #e9ecef; + color: #006064; +} + +.btn-icon.delete:hover { + padding: 1rem; + border-bottom: 1px solid #e9ecef; +} + +.filter-container { + padding: 1rem; + border-bottom: 1px solid #e9ecef; +} + +select { + width: 100%; + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; + color: #333; + background-color: white; +} + +.tasks-container { + padding: 1rem; +} + +.no-tasks { + text-align: center; + color: #6c757d; + padding: 1rem; + font-style: italic; +} diff --git a/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.ts b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.ts new file mode 100644 index 0000000..0d96027 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-card/project-card.component.ts @@ -0,0 +1,111 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { TaskCardComponent } from '../task-card/task-card.component'; +import { TaskFormModalComponent } from '../task-form-modal/task-form-modal.component'; +import { Project, Task } from '../../models/task.interface'; +import { TaskService } from '../../services/task.service'; +import { TaskStatusFilterPipe } from '../../pipes/task-status-filter.pipe'; + +@Component({ + selector: 'app-project-card', + standalone: true, + imports: [CommonModule, FormsModule, TaskCardComponent, TaskFormModalComponent, TaskStatusFilterPipe], + templateUrl: './project-card.component.html', + styleUrl: './project-card.component.scss' +}) +export class ProjectCardComponent implements OnInit { + @Input() project: Project; + @Output() edit = new EventEmitter(); + @Output() delete = new EventEmitter(); + + tasks: Task[] = []; + showTaskModal = false; + selectedTask?: Task; + isExpanded = false; + selectedStatus: 'All' | 'Pending' | 'In Progress' | 'Done' = 'All'; + + constructor(private taskService: TaskService) { } + + ngOnInit() { + this.loadTasks(); + } + + toggleExpand() { + this.isExpanded = !this.isExpanded; + } + + loadTasks() { + if (this.project.id) { + this.taskService.getTasks(this.project.id).subscribe( + (tasks) => { + this.tasks = tasks; + + //Explicitly reset selectedStatus to trigger pipe binding + this.selectedStatus = 'All'; + }, + (error) => console.error('Error loading tasks: ', error) + ); + } + } + + showTaskForm() { + this.selectedTask = undefined; + this.showTaskModal = true; + } + + editTask(task: Task) { + this.selectedTask = task; + this.showTaskModal = true; + } + + saveTask(task: Task) { + if (this.project.id) { + if (task.id) { + this.taskService.updateTask(this.project.id, task.id, task).subscribe( + () => { + this.loadTasks(); + this.closeTaskModal(); + }, + error => console.error('Error updating task: ', error) + ); + } else { + this.taskService.createTask(this.project.id, task).subscribe( + () => { + this.loadTasks(); + this.closeTaskModal(); + }, + error => console.error('Error creating task: ', error) + ); + } + } + } + + deleteTask(taskId: string) { + const confirmDelete = confirm(`Are you sure you want to delete this task?`); + if (confirmDelete) { + if (this.project.id) { + this.taskService.deleteTask(this.project.id, taskId).subscribe( + () => this.loadTasks(), + error => console.error('Error deleting task: ', error) + ); + } + } + + } + + closeTaskModal() { + this.showTaskModal = false; + this.selectedTask = undefined; + } + + editProject() { + this.edit.emit(this.project); + } + + deleteProject() { + if (this.project.id) { + this.delete.emit(this.project.id); + } + } +} diff --git a/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.html b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.html new file mode 100644 index 0000000..559ef11 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.html @@ -0,0 +1,36 @@ + diff --git a/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.scss b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.scss new file mode 100644 index 0000000..da67a27 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.scss @@ -0,0 +1,93 @@ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: white; + padding: 2rem; + border-radius: 8px; + width: 90%; + max-width: 500px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +h2 { + margin: 0 0 1.5rem; + color: #006064; +} + +.form-group { + margin-bottom: 1.5rem; +} + +label { + display: inline-block; + padding: 0.75rem; + border: none; + border-radius: 4px; + font-size: 1rem; +} + +input, +select { + width: 100%; + padding: 0.75rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; +} + +.error { + color: #f44336; + font-size: 0.875rem; + margin-top: 0.25rem; +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 1rem; +} + +button { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 4px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.btn-primary { + background: #006064; + color: white; +} + +.btn-primary:hover { + background: #004d40; +} + +.btn-secondary { + background: #e0e0e0; + color: #333; +} + +.btn-secondary:hover { + background: #d5d5d5; +} + +button:disabled, +button:disabled:hover { + opacity: 0.7; + cursor: not-allowed; + background: #f44336; +} diff --git a/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.ts b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.ts new file mode 100644 index 0000000..a02de25 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-form-modal/project-form-modal.component.ts @@ -0,0 +1,63 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Project } from '../../models/task.interface'; + +@Component({ + selector: 'app-project-form-modal', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], + templateUrl: './project-form-modal.component.html', + styleUrl: './project-form-modal.component.scss' +}) +export class ProjectFormModalComponent implements OnInit { + @Input() project?: Project; + @Output() save = new EventEmitter(); + @Output() closeModal = new EventEmitter(); + + projectForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.projectForm = this.fb.group({ + projectTitle: ['', Validators.required] + }); + } + + ngOnInit() { + if (this.project) { + this.projectForm.patchValue({ + projectTitle: this.project.projectTitle + }); + } + } + + onSubmit() { + if (this.projectForm.valid) { + const projectData: Project = { + id: this.project?.id, + projectTitle: this.projectForm.get('projectTitle')?.value + }; + this.save.emit(projectData); + } + } + + onBackdropClick() { + if (confirm('Are you sure you want to exit without saving?')) { + this.closeModal.emit(); + } + } + + onCloseClick() { + if (confirm('Are you sure you want to exit without saving?')) { + this.closeModal.emit(); + } + } + + @HostListener('document:keydown.escape', ['$event']) + onEscPressed(event: KeyboardEvent) { + if (confirm('Are you sure you want to exit without saving?')) { + this.closeModal.emit(); + } + } + +} diff --git a/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.html b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.html new file mode 100644 index 0000000..50f261b --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.html @@ -0,0 +1,25 @@ +
+
+

Task Planner

+ +
+ +
+
+ No projects yet. Create a project to get started! +
+ +
+ + +
diff --git a/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.scss b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.scss new file mode 100644 index 0000000..357243e --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.scss @@ -0,0 +1,49 @@ +.project-list { + margin: 0 auto; + padding: 2 rem; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +h1 { + margin: 0; + color: #006064; +} + +.btn-primary { + background: #006064; + color: white; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + transition: background-color 0.2s; +} + +.btn-primary:hover { + background: #004d40; +} + +.projects-container { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.no-projects { + text-align: center; + color: #6c757d; + padding: 2rem; + font-style: italic; + background: #f8f9fa; + border-radius: 8px; +} diff --git a/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.ts b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.ts new file mode 100644 index 0000000..4a15f4e --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/project-list/project-list.component.ts @@ -0,0 +1,78 @@ +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Project } from '../../models/task.interface'; +import { TaskService } from '../../services/task.service'; +import { ProjectCardComponent } from '../project-card/project-card.component'; +import { ProjectFormModalComponent } from '../project-form-modal/project-form-modal.component'; + +@Component({ + selector: 'app-project-list', + standalone: true, + imports: [CommonModule, ProjectCardComponent, ProjectFormModalComponent], + templateUrl: './project-list.component.html', + styleUrl: './project-list.component.scss' +}) +export class ProjectListComponent implements OnInit { + projects: Project[] = []; + showProjectModal = false; + selectedProject?: Project; + + constructor(private taskService: TaskService) { } + + ngOnInit() { + this.loadProjects(); + } + + loadProjects() { + this.taskService.getProjects().subscribe( + projects => this.projects = projects, + error => console.error('Error loading projects: ', error) + ); + } + + showProjectForm() { + this.selectedProject = undefined; + this.showProjectModal = true; + } + + editProject(project: Project) { + this.selectedProject = project; + this.showProjectModal = true; + } + + saveProject(project: Project) { + if (project.id) { + this.taskService.updateProject(project.id, project).subscribe( + () => { + this.loadProjects(); + this.closeProjectModal(); + }, + error => console.error('Error updating project: ', error) + ); + } else { + this.taskService.createProject(project).subscribe( + () => { + this.loadProjects(); + this.closeProjectModal(); + }, + error => console.error('Error creating project: ', error) + ); + } + } + + deleteProject(projectId: string) { + const confirmDelete = confirm(`Are you sure you want to delete the project?`) + if (confirmDelete) { + this.taskService.deleteProject(projectId).subscribe( + () => this.loadProjects(), + error => console.error('Error deleting project: ', error) + ); + } + } + + closeProjectModal() { + this.showProjectModal = false; + this.selectedProject = undefined; + } + +} diff --git a/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.html b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.html new file mode 100644 index 0000000..875639f --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.html @@ -0,0 +1,43 @@ +
+
+
+

{{ task.title }}

+ + {{ task.status }} + +
+
+ + + +
+
+
+
+ Due Date: + {{ task.dueDate }} +
+
+ Status: + {{ task.status }} +
+
+
diff --git a/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.scss b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.scss new file mode 100644 index 0000000..6631b29 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.scss @@ -0,0 +1,96 @@ +.task-card { + background: white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + margin-bottom: 1rem; + transition: all 0.3s ease; +} + +.task-card-expanded { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +.task-header { + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; +} + +.task-title { + display: flex; + align-items: center; + gap: 1rem; +} + +h3 { + margin: 0; + color: #333; + font-size: 1.1rem; +} + +.status-badge.pending { + background: #fff3e0; + color: #e65100; +} + +.status-badge.in-progress { + background: #e3f5e9; + color: #1565c0; +} + +.status-badge.done { + background: #e8f5e9; + color: #2e7d32; +} + +.task-actions { + display: flex; + gap: 0.5rem; +} + +.btn-icon { + background: none; + border: none; + cursor: pointer; + padding: 0.25rem; + font-size: 1.25rem; + opacity: 0.7; + transition: opacity 0.2s; +} + +.btn-icon:hover { + opacity: 1; +} + +.task-details { + padding: 1rem; + border-top: 1px solid #eee; + background: #f9f9f9; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} + +.detail-item { + display: flex; + margin-bottom: 0.5rem; +} + +.detail-item:last-child { + margin-bottom: 0; +} + +.label { + font-weight: 500; + color: #666; + width: 100px; +} + +.value { + color: #333; +} + +.btn-icon.delete:hover { + color: #dc3545; +} diff --git a/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.ts b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.ts new file mode 100644 index 0000000..fe4beaf --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-card/task-card.component.ts @@ -0,0 +1,24 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { OverdueHighlighterDirective } from '../../directives/overdue-highlighter.directive'; +import { Task } from '../../models/task.interface'; + +@Component({ + selector: 'app-task-card', + standalone: true, + imports: [CommonModule, OverdueHighlighterDirective], + templateUrl: './task-card.component.html', + styleUrl: './task-card.component.scss' +}) +export class TaskCardComponent { + @Input() task: Task; + @Output() edit = new EventEmitter(); + @Output() delete = new EventEmitter(); + + isExpanded = false; + + toggleExpand() { + this.isExpanded = !this.isExpanded; + } + +} diff --git a/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.html b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.html new file mode 100644 index 0000000..c421d84 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.html @@ -0,0 +1,64 @@ + diff --git a/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.scss b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.scss new file mode 100644 index 0000000..53f1e75 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.scss @@ -0,0 +1,93 @@ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: white; + padding: 2rem; + border-radius: 8px; + width: 90%; + max-width: 500px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +h2 { + margin: 0 0 1.5rem; + color: #006064; +} + +.form-group { + margin-bottom: 1.5rem; +} + +label { + display: block; + padding: 0.75rem; + border: none; + border-radius: 4px; + font-size: 1rem; +} + +input, +select { + width: 100%; + padding: 0.75rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 1rem; +} + +.error { + color: #f44336; + font-size: 0.875rem; + margin-top: 0.25rem; +} + +.button-group { + display: flex; + justify-content: flex-end; + gap: 1rem; +} + +button { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 4px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.btn-primary { + background: #006064; + color: white; +} + +.btn-primary:hover { + background: #004d40; +} + +.btn-secondary { + background: #e0e0e0; + color: #333; +} + +.btn-secondary:hover { + background: #d5d5d5; +} + +button:disabled, +button:disabled:hover { + opacity: 0.7; + cursor: not-allowed; + background: #f44336; +} diff --git a/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.ts b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.ts new file mode 100644 index 0000000..fd40783 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/components/task-form-modal/task-form-modal.component.ts @@ -0,0 +1,85 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Task } from '../../models/task.interface'; + +@Component({ + selector: 'app-task-form-modal', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], + templateUrl: './task-form-modal.component.html', + styleUrl: './task-form-modal.component.scss' +}) +export class TaskFormModalComponent implements OnInit { + @Input() task?: Task; + @Output() save = new EventEmitter(); + @Output() closeModal = new EventEmitter(); + + taskForm: FormGroup; + parseddueDate: string = ''; + + constructor(private fb: FormBuilder) { + this.taskForm = this.fb.group({ + title: ['', Validators.required], + dueDate: ['', [Validators.required, Validators.pattern(/^(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/\d{2}$/)]], + status: ['Pending', Validators.required] + }); + } + + ngOnInit() { + if (this.task) { + this.taskForm.patchValue({ + title: this.task.title, + dueDate: this.task.dueDate, + status: this.task.status + }); + + const [mm, dd, yy] = this.task.dueDate.split('/'); + this.parseddueDate = `20${yy}-${mm.padStart(2, '0')}-${dd.padStart(2, '0')}`; + } + } + + onDatechange(event: Event) { + const input = event.target as HTMLInputElement; + const value = input.value; + if (value) { + const [yyyy, mm, dd] = value.split('-'); + const formatted = `${mm}/${dd}/${yyyy.slice(2)}`; + this.taskForm.get('dueDate')?.setValue(formatted); + this.taskForm.get('dueDate')?.markAsTouched(); + } + this.parseddueDate = value; + } + + onSubmit() { + if (this.taskForm.valid) { + const taskData: Task = { + id: this.task?.id, + title: this.taskForm.get('title')?.value, + dueDate: this.taskForm.get('dueDate')?.value, + status: this.taskForm.get('status')?.value || 'Pending', + projectId: this.task?.projectId + }; + this.save.emit(taskData); + } + } + + onBackdropClick() { + if (confirm('Are you sure you want to close without saving?')) { + this.closeModal.emit(); + } + } + + onCloseClick() { + if (confirm('Are you sure you want to exit without saving?')) { + this.closeModal.emit(); + } + } + + @HostListener('document:keydown.escape', ['$event']) + onEscPressed(event: KeyboardEvent) { + if (confirm('Are you sure you want to exit without saving?')) { + this.closeModal.emit(); + } + } +} diff --git a/projects/taskplanner-angular-app/src/app/directives/overdue-highlighter.directive.ts b/projects/taskplanner-angular-app/src/app/directives/overdue-highlighter.directive.ts new file mode 100644 index 0000000..7703bec --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/directives/overdue-highlighter.directive.ts @@ -0,0 +1,29 @@ +import { Directive, OnInit, ElementRef, Input } from "@angular/core"; +import { Task } from "../models/task.interface"; + +@Directive({ + selector: '[appOverdue]', + standalone: true +}) +export class OverdueHighlighterDirective implements OnInit { + @Input() task!: Task; + + constructor(private el: ElementRef) { } + + ngOnInit() { + if (this.isOverDue()) { + this.el.nativeElement.style.backgroundColor = '#ffebee'; + this.el.nativeElement.style.borderLeft = '4px solid #f44336'; + } + } + + private isOverDue(): boolean { + if (this.task.status === 'Done') return false; + + const today = new Date(); + const [month, day, year] = this.task.dueDate.split('/'); + const dueDate = new Date(2000 + parseInt(year), parseInt(month) - 1, parseInt(day)); + + return dueDate < today; + } +} \ No newline at end of file diff --git a/projects/taskplanner-angular-app/src/app/models/task.interface.ts b/projects/taskplanner-angular-app/src/app/models/task.interface.ts new file mode 100644 index 0000000..a61fc5d --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/models/task.interface.ts @@ -0,0 +1,14 @@ +// Task interface for defining structure of tasks +export interface Task { + id?: string; + title: string; + dueDate: string; + status: 'Pending' | 'In Progress' | 'Done'; + projectId?: string; +} + +// Project interface for defining structure of projects +export interface Project { + id?: string; + projectTitle: string; +} \ No newline at end of file diff --git a/projects/taskplanner-angular-app/src/app/pipes/task-status-filter.pipe.ts b/projects/taskplanner-angular-app/src/app/pipes/task-status-filter.pipe.ts new file mode 100644 index 0000000..52cb202 --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/pipes/task-status-filter.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from "@angular/core"; +import { Task } from "../models/task.interface"; + +@Pipe({ + name: 'taskStatusFilter', + standalone: true +}) + +export class TaskStatusFilterPipe implements PipeTransform { + transform(tasks: Task[], status: 'Pending' | 'In Progress' | 'Done' | 'All'): Task[] { + if (!tasks || !status || status == 'All') { + return tasks; + } + return tasks.filter(task => task.status === status); + } +} \ No newline at end of file diff --git a/projects/taskplanner-angular-app/src/app/services/task.service.ts b/projects/taskplanner-angular-app/src/app/services/task.service.ts new file mode 100644 index 0000000..45f220f --- /dev/null +++ b/projects/taskplanner-angular-app/src/app/services/task.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from "@angular/core"; +import { HttpClient } from "@angular/common/http"; +import { Observable, map } from "rxjs"; +import { Project, Task } from "../models/task.interface"; + +@Injectable({ + providedIn: 'root' +}) +export class TaskService { + private apiUrl = 'http://localhost:3000'; + + constructor(private http: HttpClient) { } + + // Project CRUD operations + getProjects(): Observable { + return this.http.get(`${this.apiUrl}/projects`); + } + + createProject(project: Project): Observable { + return this.http.post(`${this.apiUrl}/projects`, project); + } + + updateProject(id: string, project: Project): Observable { + return this.http.put(`${this.apiUrl}/projects/${id}`, project); + } + + deleteProject(id: string): Observable { + return this.http.delete(`${this.apiUrl}/projects/${id}`); + } + + // Task CRUD operations + getTasks(projectId: string): Observable { + return this.http.get(`${this.apiUrl}/tasks`).pipe( + map(tasks => tasks.filter(task => task.projectId === projectId)) + ); + } + + createTask(projectId: string, task: Task): Observable { + const taskWithProjectId = { ...task, projectId }; + return this.http.post(`${this.apiUrl}/tasks`, taskWithProjectId); + } + + updateTask(projectId: string, taskId: string, task: Task): Observable { + const taskWithProjectId = { ...task, projectId }; + return this.http.put(`${this.apiUrl}/tasks/${taskId}`, taskWithProjectId); + } + + deleteTask(projectId: string, taskId: string): Observable { + return this.http.delete(`${this.apiUrl}/tasks/${taskId}`); + } +} \ No newline at end of file diff --git a/projects/taskplanner-angular-app/src/index.html b/projects/taskplanner-angular-app/src/index.html new file mode 100644 index 0000000..3d73aa1 --- /dev/null +++ b/projects/taskplanner-angular-app/src/index.html @@ -0,0 +1,17 @@ + + + + + TaskplannerAngularApp + + + + + + + + + diff --git a/projects/taskplanner-angular-app/src/main.ts b/projects/taskplanner-angular-app/src/main.ts new file mode 100644 index 0000000..35b00f3 --- /dev/null +++ b/projects/taskplanner-angular-app/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/projects/taskplanner-angular-app/src/styles.scss b/projects/taskplanner-angular-app/src/styles.scss new file mode 100644 index 0000000..3da9dfe --- /dev/null +++ b/projects/taskplanner-angular-app/src/styles.scss @@ -0,0 +1,16 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; + line-height: 1.6; + color: #333; +} + +button { + font-family: inherit; +} diff --git a/projects/taskplanner-angular-app/tsconfig.app.json b/projects/taskplanner-angular-app/tsconfig.app.json new file mode 100644 index 0000000..18903c3 --- /dev/null +++ b/projects/taskplanner-angular-app/tsconfig.app.json @@ -0,0 +1,18 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [], + }, + "angularCompilerOptions": { + "strict": false + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/projects/taskplanner-angular-app/tsconfig.spec.json b/projects/taskplanner-angular-app/tsconfig.spec.json new file mode 100644 index 0000000..0a43b83 --- /dev/null +++ b/projects/taskplanner-angular-app/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 56b644d..008d7d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compileOnSave": false, "compilerOptions": { "outDir": "./dist/out-tsc", - "strict": true, + "strict": false, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true,