From 566148dece7d0e3ad46364683fef622b1752c78e Mon Sep 17 00:00:00 2001 From: lisaaksionova bc2024-6 Date: Mon, 2 Dec 2024 17:15:46 +0200 Subject: [PATCH] init --- package-lock.json | 410 ++++++++++++++++++ package.json | 4 + public/index.html | 2 +- src/App.css | 41 +- src/App.test.tsx | 9 - src/App.tsx | 107 ++++- .../CreateTask/CreateTask.component.tsx | 17 + .../CreateTask/CreateTask.styles.scss | 18 + src/features/Task/Task.component.tsx | 52 +++ src/features/Task/Task.styles.scss | 59 +++ src/features/TaskLine/TaskLine.component.tsx | 47 ++ src/features/TaskLine/TaskLine.styles.scss | 24 + .../TaskModal/TaskModal.component.tsx | 60 +++ src/features/TaskModal/TaskModal.styles.scss | 73 ++++ src/index.css | 3 +- src/models/Task.ts | 8 + src/models/TaskModal.ts | 4 + src/models/TaskStatus.ts | 5 + src/setupTests.ts | 5 - 19 files changed, 880 insertions(+), 68 deletions(-) delete mode 100644 src/App.test.tsx create mode 100644 src/features/CreateTask/CreateTask.component.tsx create mode 100644 src/features/CreateTask/CreateTask.styles.scss create mode 100644 src/features/Task/Task.component.tsx create mode 100644 src/features/Task/Task.styles.scss create mode 100644 src/features/TaskLine/TaskLine.component.tsx create mode 100644 src/features/TaskLine/TaskLine.styles.scss create mode 100644 src/features/TaskModal/TaskModal.component.tsx create mode 100644 src/features/TaskModal/TaskModal.styles.scss create mode 100644 src/models/Task.ts create mode 100644 src/models/TaskModal.ts create mode 100644 src/models/TaskStatus.ts delete mode 100644 src/setupTests.ts diff --git a/package-lock.json b/package-lock.json index 040774e..61edd7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "todolist", "version": "0.1.0", "dependencies": { + "@dnd-kit/core": "^6.2.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -20,6 +21,9 @@ "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "sass": "^1.81.0" } }, "node_modules/@adobe/css-tools": { @@ -2233,6 +2237,42 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.2.0.tgz", + "integrity": "sha512-KVK/CJmaYGTxTPU6P0+Oy4itgffTUa80B8317sXzfOr1qUzSL29jE7Th11llXiu2haB7B9Glpzo2CDElin+geQ==", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -2862,6 +2902,302 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -6144,6 +6480,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8496,6 +8845,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "devOptional": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -10691,6 +11046,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -13423,6 +13785,26 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", + "devOptional": true, + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", @@ -13460,6 +13842,34 @@ } } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "devOptional": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "devOptional": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index f7c30c8..69b54db 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@dnd-kit/core": "^6.2.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -39,5 +40,8 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "sass": "^1.81.0" } } diff --git a/public/index.html b/public/index.html index aa069f2..b54c450 100644 --- a/public/index.html +++ b/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + My Todo List diff --git a/src/App.css b/src/App.css index 74b5e05..db7a439 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,7 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; +.App{ display: flex; flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} + justify-content: space-between; + height: 100vh; + background-color: #e5e5dc; +} \ No newline at end of file diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 2a68616..0000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/App.tsx b/src/App.tsx index a53698a..da63e48 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,99 @@ -import React from 'react'; -import logo from './logo.svg'; +import React, {useState} from 'react'; import './App.css'; +import TaskLine from './features/TaskLine/TaskLine.component'; +import TaskModal from './features/TaskModal/TaskModal.component'; +import { TaskStatus } from './models/TaskStatus'; +import { Task } from './models/Task'; +import { DndContext, DragEndEvent } from '@dnd-kit/core'; function App() { + const [tasks, setTasks] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedTask, setSelectedTask] = useState(null); + + + const handleOpenModal = () => setIsModalOpen(true); + const handleCloseModal = () => setIsModalOpen(false); + + const handleSaveTask = (task: { id?: number; title: string; text: string }) => { + if (task.id) { + // Editing an existing task + setTasks((prevTasks) => + prevTasks.map((t) => + t.id === task.id ? { ...t, title: task.title, text: task.text } : t + ) + ); + } else { + // Adding a new task + const newTask: Task = { + id: tasks.length + 1, + title: task.title, + text: task.text, + status: TaskStatus.Pending, + }; + setTasks([...tasks, newTask]); + } + setSelectedTask(null); + setIsModalOpen(false); + }; + + + function handleDragEnd(event: DragEndEvent){ + const {active, over} = event; + + if (!over) return; + + const taskId = active.id as number; + const newStatus = over.id as Task['status']; + + setTasks(() => tasks.map(task => task.id === taskId ? { + ...task, + status: newStatus, + } : task)) + } + + const handleDeleteTask = (id: number) => { + console.log("Deleting task with id:", id); + setTasks((prevTasks) => prevTasks.filter((task) => task.id !== id)); + }; + + const handleEditTask = (task: Task) => { + setSelectedTask(task); + setIsModalOpen(true); + }; + + return (
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
+ + task.status === TaskStatus.Pending)} + onCreateTask={handleOpenModal} + onDeleteTask={handleDeleteTask} + onEditTask={handleEditTask} +/> + task.status === TaskStatus.InProgress)} + onDeleteTask={handleDeleteTask} + onEditTask={handleEditTask} +/> + task.status === TaskStatus.Done)} + onDeleteTask={handleDeleteTask} + onEditTask={handleEditTask} +/> + + + +
); } diff --git a/src/features/CreateTask/CreateTask.component.tsx b/src/features/CreateTask/CreateTask.component.tsx new file mode 100644 index 0000000..1b6531d --- /dev/null +++ b/src/features/CreateTask/CreateTask.component.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import './CreateTask.styles.scss'; + +type Props = { + onOpenModal: () => void; +}; + + +const CreateTask = ({ onOpenModal }: Props) => { + return ( +
+

+

+
+ ); +}; + +export default CreateTask; diff --git a/src/features/CreateTask/CreateTask.styles.scss b/src/features/CreateTask/CreateTask.styles.scss new file mode 100644 index 0000000..dc8a52d --- /dev/null +++ b/src/features/CreateTask/CreateTask.styles.scss @@ -0,0 +1,18 @@ +.createtask-card{ + display: flex; + width: 180px; + height: 200px; + background-color: #f5f0e1; + border-radius: 8px; + overflow: hidden; + align-items: center; + justify-content: center; + opacity: 0.78; +} +.createtask-card:hover{ + opacity: 1; +} +.createtask-card > p { + color: #1e3d59; + font-size: 60px; +} \ No newline at end of file diff --git a/src/features/Task/Task.component.tsx b/src/features/Task/Task.component.tsx new file mode 100644 index 0000000..cd9fe5b --- /dev/null +++ b/src/features/Task/Task.component.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import './Task.styles.scss'; +import { Task as TaskType } from '../../models/Task'; +import { useDraggable } from '@dnd-kit/core'; +import { TaskStatus } from '../../models/TaskStatus'; +import { stat } from 'fs/promises'; + +interface Props { + id: number; + title: string; + text: string; + status: TaskStatus; + deleteTask: () => void; // Function to delete the task + onEdit: () => void; // Function to edit the task +}; + + +const Task= ({ id, title, text, status, deleteTask, onEdit } : Props) => { + + const {attributes, listeners, setNodeRef, transform} = useDraggable({id: id}); + + const style = transform ? { + position: 'absolute' as 'absolute', + zIndex: '1000', + transform: `translate(${transform.x}px, ${transform.y}px)`, + } : undefined; + + // const headerColor = status === TaskStatus.Pending ? "#5c3c92" : + // status === TaskStatus.InProgress ? "#ff6e40" : "#c4a35a"; + + return ( +
+
+

{title}

+
+
+

{text}

+
+
+ + +
+
+ ) +} + +export default Task; \ No newline at end of file diff --git a/src/features/Task/Task.styles.scss b/src/features/Task/Task.styles.scss new file mode 100644 index 0000000..bcd0220 --- /dev/null +++ b/src/features/Task/Task.styles.scss @@ -0,0 +1,59 @@ +.task-card{ + display: flex; + flex-direction: column; + width: 180px; + height: 200px; + background-color: #f5f0e1; + border-radius: 8px; + box-shadow: 3px 3px 3px red; + overflow: hidden; + // position: fixed; +} + +.task-card:active{ + box-shadow: 5px 5px 5px rgba(255, 0, 0, 0.563); +} + +.task-header{ + background-color: #ff6e40; + color: white; + padding: 0.65px; + font-size: 13px; + font-weight: bold; + text-align: center; + flex-shrink: 0; +} + +.task-textarea{ + padding: 6px; + font-size: 13px; + color: #333; + text-align: center; + overflow: hidden; + flex-grow: 1; +} + +.task-footer{ + display: flex; + justify-content: space-between; + background-color: #ffc13b; + color: #555; + padding: 6px; + text-align: center; + border-top: 1px solid #ddd; + flex-shrink: 0; +} + +.button{ + width: 57px; + height: 30px; + margin: 0 7px; + background-color: #f5f0e1; + border: 1.25px solid #ff6e40; + border-radius: 6px; + color: #1e3d59; + font-weight: bold; +} +.button:hover{ + background-color: #f5f0e1ba; +} \ No newline at end of file diff --git a/src/features/TaskLine/TaskLine.component.tsx b/src/features/TaskLine/TaskLine.component.tsx new file mode 100644 index 0000000..e8198ce --- /dev/null +++ b/src/features/TaskLine/TaskLine.component.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import './TaskLine.styles.scss'; +import Task from '../Task/Task.component'; +import CreateTask from '../CreateTask/CreateTask.component'; +import { TaskStatus } from '../../models/TaskStatus'; +import { Task as TaskType } from '../../models/Task'; +import { useDroppable } from '@dnd-kit/core'; + +type Props = { + taskLineName: string; + tasks: TaskType[]; + onCreateTask?: () => void; + onDeleteTask: (id: number) => void; + onEditTask: (task: TaskType) => void; +} + +const TaskLine = ({ taskLineName, tasks, onCreateTask, onDeleteTask, onEditTask} : Props) => { + + const backgroundColor = + taskLineName === "Pending" ? "#077b8a" : + taskLineName === "In Progress" ? "#e0a96d" : "#77c593" ; + + const { setNodeRef } = useDroppable({id: taskLineName}); + + return ( + +
+

{taskLineName}

+
+ {tasks.map((task) => ( + onDeleteTask(task.id)} + onEdit={() => onEditTask(task)} + /> + ))} + {taskLineName === "Pending" && onCreateTask && ( + + )} +
+
+ + + ) +} +export default TaskLine; \ No newline at end of file diff --git a/src/features/TaskLine/TaskLine.styles.scss b/src/features/TaskLine/TaskLine.styles.scss new file mode 100644 index 0000000..8954e3e --- /dev/null +++ b/src/features/TaskLine/TaskLine.styles.scss @@ -0,0 +1,24 @@ +.taskline{ + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: flex-start; + gap: 45px; + padding: 0 20px 20px; + flex-wrap: nowrap; + overflow: hidden; +} + +.tasklist-container{ + display: flex; + flex-direction: column; + height: 282.23px; + width: 100%; + overflow: hidden; +} + + +.tasklist-name{ + padding-left: 20px; + color: #f5f0e1; +} \ No newline at end of file diff --git a/src/features/TaskModal/TaskModal.component.tsx b/src/features/TaskModal/TaskModal.component.tsx new file mode 100644 index 0000000..69c05bf --- /dev/null +++ b/src/features/TaskModal/TaskModal.component.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from 'react'; +import './TaskModal.styles.scss'; +import { Task } from '../../models/Task'; + +interface Props { + isOpen: boolean; + onClose: () => void; + onSave: (task: {id?: number, title: string; text: string }) => void; + task: Task | null; +} + +const TaskModal = ({ isOpen, onClose, onSave, task }: Props) => { + const [title, setTitle] = useState(task?.title || ''); // Pre-fill for editing + const [text, setText] = useState(task?.text || ''); + + useEffect(() => { + if (task) { + setTitle(task.title); + setText(task.text); + } + }, [task]); + + if (!isOpen) return null; + + const handleSave = () => { + if (title.trim()) { + onSave({ id: task?.id, title, text }); + setTitle(''); + setText(''); + onClose(); + } else { + alert("Task title is required!"); + } + }; + + return ( +
+
+
+ setTitle(e.target.value)}/> + setText(e.target.value)}/> +
+
+ + +
+
+
+ ) +} + +export default TaskModal; \ No newline at end of file diff --git a/src/features/TaskModal/TaskModal.styles.scss b/src/features/TaskModal/TaskModal.styles.scss new file mode 100644 index 0000000..a9a5aac --- /dev/null +++ b/src/features/TaskModal/TaskModal.styles.scss @@ -0,0 +1,73 @@ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */ + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; /* Ensure it appears on top */ +} +.modal-container{ + display: flex; + flex-direction: column; + background-color: #f5f0e1; + align-items: center; + justify-content: center; + width: 480px; + height: 340px; + border-radius: 10px; + border: 1.25px solid #1e3d59; + position: relative; +} +.edit-section{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + gap: 30px; + width: 100%; + height: 80%; + padding-top: 57px; +} + +.edit-section > input{ + width: 80%; + padding: 10px; + margin: 10px 17px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + outline: none; + transition: border-color 0.3s, box-shadow 0.3s; +} +.edit-section > input:hover { + border-color: #1e3d59; +} +.edit-section > input:focus { + border-color: #1e3d59; + box-shadow: 0 0 5px rgba(74, 132, 194, 0.235); +} +.footer{ + display: flex; + justify-content: space-between; + padding: 6px; + width: 60%; + height: 20%; + margin: 10px; +} + +.button{ + width: 93px; + height: 40px; + background-color: #facd6b; + border: 1.25px solid #ff6e40; + border-radius: 6px; + color: #1e3d59; + font-weight: bold; +} +.button:hover{ + background-color: #d3b26c; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e..79f7261 100644 --- a/src/index.css +++ b/src/index.css @@ -5,9 +5,10 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + overflow: hidden; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; -} +} \ No newline at end of file diff --git a/src/models/Task.ts b/src/models/Task.ts new file mode 100644 index 0000000..162a85c --- /dev/null +++ b/src/models/Task.ts @@ -0,0 +1,8 @@ +import { TaskStatus } from "./TaskStatus"; + +export type Task = { + id: number; + title: string ; + text: string ; + status: TaskStatus; +}; \ No newline at end of file diff --git a/src/models/TaskModal.ts b/src/models/TaskModal.ts new file mode 100644 index 0000000..b2aad34 --- /dev/null +++ b/src/models/TaskModal.ts @@ -0,0 +1,4 @@ +export type TaskModal = { + isOpen: boolean; + onClose: void; +}; \ No newline at end of file diff --git a/src/models/TaskStatus.ts b/src/models/TaskStatus.ts new file mode 100644 index 0000000..65f929e --- /dev/null +++ b/src/models/TaskStatus.ts @@ -0,0 +1,5 @@ +export enum TaskStatus { + Pending = "Pending", + InProgress = "In Progress", + Done = "Done", +} \ No newline at end of file diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom';