nicholas/storage-manager#1: use tailwind; implement edit, delete, create boxes and items
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@
|
||||
<title>Storage Manager</title>
|
||||
</head>
|
||||
|
||||
<body class="scroll-container">
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
|
||||
Generated
+509
-57
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "storage-manager-client",
|
||||
"name": "client",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "storage-manager-client",
|
||||
"name": "client",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"axios": "^1.8.4",
|
||||
"color": "^5.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"tailwindcss": "^4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
@@ -328,7 +330,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -345,7 +346,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -362,7 +362,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -379,7 +378,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -396,7 +394,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -413,7 +410,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -430,7 +426,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -447,7 +442,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -464,7 +458,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -481,7 +474,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -498,7 +490,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -515,7 +506,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -532,7 +522,6 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -549,7 +538,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -566,7 +554,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -583,7 +570,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -600,7 +586,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -617,7 +602,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -634,7 +618,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -651,7 +634,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -668,7 +650,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -685,7 +666,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -702,7 +682,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -719,7 +698,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -736,7 +714,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1036,7 +1013,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1050,7 +1026,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1064,7 +1039,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1078,7 +1052,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1092,7 +1065,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1106,7 +1078,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1120,7 +1091,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1134,7 +1104,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1148,7 +1117,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1162,7 +1130,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1176,7 +1143,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1190,7 +1156,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1204,7 +1169,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1218,7 +1182,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1232,7 +1195,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1246,7 +1208,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1260,7 +1221,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1274,7 +1234,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1288,7 +1247,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1302,13 +1260,236 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz",
|
||||
"integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"enhanced-resolve": "^5.18.1",
|
||||
"jiti": "^2.4.2",
|
||||
"lightningcss": "1.29.2",
|
||||
"tailwindcss": "4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz",
|
||||
"integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.1.3",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.1.3",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.1.3",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.1.3",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.3",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.1.3",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.1.3",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.1.3",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.3",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz",
|
||||
"integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz",
|
||||
"integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz",
|
||||
"integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz",
|
||||
"integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz",
|
||||
"integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz",
|
||||
"integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz",
|
||||
"integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz",
|
||||
"integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz",
|
||||
"integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz",
|
||||
"integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz",
|
||||
"integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.3.tgz",
|
||||
"integrity": "sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.1.3",
|
||||
"@tailwindcss/oxide": "4.1.3",
|
||||
"tailwindcss": "4.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.2.0 || ^6"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -1358,7 +1539,6 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
@@ -1757,6 +1937,15 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -1778,6 +1967,19 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
||||
"integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
@@ -1827,7 +2029,6 @@
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
|
||||
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -2176,7 +2377,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -2281,6 +2481,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -2397,6 +2603,15 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
|
||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -2488,6 +2703,234 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz",
|
||||
"integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-darwin-arm64": "1.29.2",
|
||||
"lightningcss-darwin-x64": "1.29.2",
|
||||
"lightningcss-freebsd-x64": "1.29.2",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.29.2",
|
||||
"lightningcss-linux-arm64-gnu": "1.29.2",
|
||||
"lightningcss-linux-arm64-musl": "1.29.2",
|
||||
"lightningcss-linux-x64-gnu": "1.29.2",
|
||||
"lightningcss-linux-x64-musl": "1.29.2",
|
||||
"lightningcss-win32-arm64-msvc": "1.29.2",
|
||||
"lightningcss-win32-x64-msvc": "1.29.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz",
|
||||
"integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz",
|
||||
"integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz",
|
||||
"integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz",
|
||||
"integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz",
|
||||
"integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz",
|
||||
"integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz",
|
||||
"integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz",
|
||||
"integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz",
|
||||
"integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.29.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz",
|
||||
"integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@@ -2575,7 +3018,6 @@
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -2691,14 +3133,12 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -2794,7 +3234,6 @@
|
||||
"version": "4.39.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz",
|
||||
"integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
@@ -2873,7 +3312,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -2905,6 +3343,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz",
|
||||
"integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -2963,7 +3416,6 @@
|
||||
"version": "6.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz",
|
||||
"integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
|
||||
+3
-1
@@ -10,10 +10,12 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"axios": "^1.8.4",
|
||||
"color": "^5.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"tailwindcss": "^4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
|
||||
@@ -17,7 +17,7 @@ export const api = axios.create({
|
||||
api.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
console.error('API error:', error);
|
||||
return Promise.reject(error);
|
||||
console.error('API error:', error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
);
|
||||
+69
-35
@@ -1,33 +1,43 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getAllBoxes, Box, BoxCard, BoxFormModal } from './features/box';
|
||||
import './styles/app.css';
|
||||
import './styles/button.css';
|
||||
import './styles/inputBox.css';
|
||||
import './styles/textArea.css';
|
||||
import './index.css';
|
||||
|
||||
const App = () => {
|
||||
const [boxes, setBoxes] = useState<Box[]>([]);
|
||||
const [isModalOpen, setModalOpen] = useState(false);
|
||||
|
||||
|
||||
const handleCreateBox = (newBox: Box) => {
|
||||
setBoxes((boxes) => [...boxes, newBox]);
|
||||
};
|
||||
const handleDeleteBox = (id: string) => {
|
||||
setBoxes((boxes) => boxes.filter(box => box._id !== id));
|
||||
}
|
||||
|
||||
|
||||
const toggleModal = () => {
|
||||
setModalOpen(!isModalOpen);
|
||||
const [isBoxFormModalOpen, setBoxFormModalOpen] = useState(false);
|
||||
const handleBoxFormModalOpen = () => {
|
||||
setBoxFormModalOpen(true);
|
||||
};
|
||||
const handleBoxFormModalClose = () => {
|
||||
setBoxFormModalOpen(false);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
setModalOpen(false);
|
||||
// box filter
|
||||
const [searchString, setSearch] = useState('');
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(event.target.value);
|
||||
};
|
||||
const filteredBoxes = boxes.filter(box => {
|
||||
const ss = searchString.toLowerCase();
|
||||
|
||||
const handleClose = () => {
|
||||
setModalOpen(false);
|
||||
};
|
||||
const matchingItems = box.items?.filter(item =>
|
||||
item.name.toLowerCase().includes(ss) ||
|
||||
item.description.toLowerCase().includes(ss)
|
||||
);
|
||||
|
||||
return (
|
||||
box.name.toLowerCase().includes(ss) ||
|
||||
box.description.toLowerCase().includes(ss) ||
|
||||
matchingItems && matchingItems.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const response = await getAllBoxes();
|
||||
@@ -44,25 +54,49 @@ const App = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BoxFormModal isOpen={isModalOpen} onCreateBox={handleCreateBox} onClose={handleClose} />
|
||||
<div className="filter-container">
|
||||
<span className='material-icons icon'>search</span>
|
||||
<input spellCheck='false' className='filter' type='search' id='box-filter' name='filter' />
|
||||
<span className='material-icons icon clear'>clear</span>
|
||||
</div>
|
||||
<div className='header-container'>
|
||||
<button className='button' onClick={toggleModal}>new box</button>
|
||||
</div>
|
||||
<div className='body-container scroll-container'>
|
||||
<ul className='list-box'>
|
||||
{boxes.map((item) => (
|
||||
<BoxCard key={item._id} box={item} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<>
|
||||
<BoxFormModal
|
||||
mode="create"
|
||||
isOpen={isBoxFormModalOpen}
|
||||
onSubmitBox={handleCreateBox}
|
||||
onClose={handleBoxFormModalClose}
|
||||
/>
|
||||
|
||||
<div className="">
|
||||
<label htmlFor="box-filter" className="text-sm font-medium text-gray-700">Search</label>
|
||||
<input
|
||||
onChange={handleSearchChange}
|
||||
spellCheck="false"
|
||||
type="search"
|
||||
id="box-filter"
|
||||
name="filter"
|
||||
className=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="">
|
||||
<button
|
||||
onClick={handleBoxFormModalOpen}
|
||||
className=""
|
||||
>
|
||||
New Box
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='box-card-list-container'>
|
||||
<ul className='box-card-list'>
|
||||
{filteredBoxes.map((item) => (
|
||||
<BoxCard
|
||||
key={item._id}
|
||||
initialBox={item}
|
||||
onDelete={handleDeleteBox}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -8,7 +8,7 @@ const ColorSelect = ({ colorData, handler, boxColor }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<label onMouseEnter={() => handleHover(true)} onMouseLeave={() => handleHover(false)} style={style} className={`color ${boxColor === colorData.title ? 'selected' : ''}`}>
|
||||
<label onMouseEnter={() => handleHover(true)} onMouseLeave={() => handleHover(false)} style={style} >
|
||||
<input checked={boxColor === colorData.title} onChange={handler} type='radio' value={colorData.title}></input>
|
||||
</label>
|
||||
)
|
||||
|
||||
@@ -4,5 +4,5 @@ export interface Box {
|
||||
_id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
items: Item[];
|
||||
items?: Item[];
|
||||
}
|
||||
@@ -1,51 +1,118 @@
|
||||
import { useState } from 'react';
|
||||
import { Box } from '../';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Box, BoxFormModal, deleteBox } from '../';
|
||||
import { Item, ItemCard, ItemFormModal } from '../../item';
|
||||
|
||||
interface BoxCardProps {
|
||||
box: Box;
|
||||
initialBox: Box;
|
||||
onDelete: (id: string) => void;
|
||||
}
|
||||
|
||||
export const BoxCard: React.FC<BoxCardProps> = ({ box }) => {
|
||||
export const BoxCard: React.FC<BoxCardProps> = ({ initialBox, onDelete }) => {
|
||||
|
||||
const [items, setItems] = useState<Item[]>(box.items);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [items, setItems] = useState<Item[]>(initialBox.items!);
|
||||
const [box, setBox] = useState<Box>(initialBox);
|
||||
|
||||
const [isBoxFormModalOpen, setBoxFormModalOpen] = useState(false);
|
||||
const [isItemFormModalOpen, setItemFormModalOpen] = useState(false);
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
menuRef.current && !menuRef.current.contains(event.target as Node) &&
|
||||
menuButtonRef.current && !menuButtonRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const toggleMenu = () => {
|
||||
setIsMenuOpen(prevState => !prevState);
|
||||
};
|
||||
|
||||
const handleOpenItemFormModal = () => {
|
||||
setModalOpen(true);
|
||||
setItemFormModalOpen(true);
|
||||
}
|
||||
const handleClose = () => {
|
||||
setModalOpen(false);
|
||||
const handleCloseItemFormModal = () => {
|
||||
setItemFormModalOpen(false);
|
||||
};
|
||||
|
||||
const handleOpenBoxFormModal = () => {
|
||||
setBoxFormModalOpen(true);
|
||||
}
|
||||
const handleCloseBoxFormModal = () => {
|
||||
setBoxFormModalOpen(false);
|
||||
};
|
||||
|
||||
|
||||
const handleCreateItem = (newItem: Item) => {
|
||||
setItems((items) => [...items, newItem]);
|
||||
};
|
||||
|
||||
const handleClickEditBox = () => {
|
||||
setBoxFormModalOpen(true);
|
||||
}
|
||||
const handleUpdateBox = (updatedBox: Box) => {
|
||||
setBox(updatedBox);
|
||||
}
|
||||
const handleDeleteBox = async () => {
|
||||
if (box._id != null) {
|
||||
const response = await deleteBox(box._id);
|
||||
if (response.success) {
|
||||
onDelete(box._id);
|
||||
} else {
|
||||
console.error('No data returned in the response');
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleDeleteItem = (id: string) => {
|
||||
setItems((items) => items.filter(item => item._id !== id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<li className="box">
|
||||
<ItemFormModal isOpen={modalOpen} onCreateItem={handleCreateItem} onClose={handleClose} boxId={box._id} />
|
||||
<div className='menu-more-container'></div>
|
||||
<div className="box-header">
|
||||
<div className="box-title-container">
|
||||
<h2 className="box-title">{box.name}</h2>
|
||||
<h3 className="box-subtitle">{box.description}</h3>
|
||||
<>
|
||||
<BoxFormModal mode="edit" initialData={box} isOpen={isBoxFormModalOpen} onSubmitBox={handleUpdateBox} onClose={handleCloseBoxFormModal} />
|
||||
<ItemFormModal mode="create" isOpen={isItemFormModalOpen} onSubmitItem={handleCreateItem} onClose={handleCloseItemFormModal} boxId={box._id!} />
|
||||
|
||||
<li className='box-card'>
|
||||
<div>
|
||||
<div>
|
||||
<h2 className='box-card-title'>{box.name}</h2>
|
||||
<h3 className='box-card-description'>{box.description}</h3>
|
||||
</div>
|
||||
<span className='menu-btn box material-icons'>more_vert</span>
|
||||
<button ref={menuButtonRef} onClick={toggleMenu}>menu</button>
|
||||
{isMenuOpen && (
|
||||
<div ref={menuRef}>
|
||||
<ul>
|
||||
<li><button onClick={handleClickEditBox}>edit</button></li>
|
||||
<li><button onClick={handleDeleteBox}>delete</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="head-divider" />
|
||||
<ul className={`list-box-item scroll-container ${box.items?.length === 0 ? 'empty' : ''}`}>
|
||||
<div className='item-card-list-container'>
|
||||
<ul className='item-card-list'>
|
||||
{items?.map((item) => (
|
||||
<ItemCard key={item._id} item={item}></ItemCard>
|
||||
<ItemCard key={item._id} boxId={box._id!} initialItem={item} onDelete={handleDeleteItem} />
|
||||
))}
|
||||
</ul>
|
||||
<div className="menu-bottom">
|
||||
<span
|
||||
className="material-icons add"
|
||||
onClick={handleOpenItemFormModal}>
|
||||
add
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={handleOpenItemFormModal}>add</button>
|
||||
</div>
|
||||
</li>
|
||||
</>
|
||||
|
||||
);
|
||||
};
|
||||
@@ -1,42 +1,40 @@
|
||||
import { useState } from 'react';
|
||||
import Modal from '../../../modal';
|
||||
import { Box, createBox } from '..';
|
||||
import { Box, createBox, updateBox } from '..';
|
||||
import { Item } from '../../item';
|
||||
|
||||
interface BoxFormModalProps {
|
||||
mode: string;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onCreateBox: (box: Box) => void;
|
||||
onSubmitBox: (box: Box) => void;
|
||||
initialData?: Partial<Box>;
|
||||
|
||||
}
|
||||
export const BoxFormModal: React.FC<BoxFormModalProps> = ({ isOpen, onClose, onCreateBox, initialData }) => {
|
||||
|
||||
export const BoxFormModal: React.FC<BoxFormModalProps> = ({ mode, initialData, isOpen, onClose, onSubmitBox }) => {
|
||||
const [boxName, setBoxName] = useState(initialData?.name ?? '');
|
||||
const [boxDescription, setBoxDescription] = useState(initialData?.description ?? '');
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
|
||||
const boxData: Box = {
|
||||
name: formData.get('name') as string,
|
||||
description: formData.get('description') as string,
|
||||
items: initialData?.items as Item[]
|
||||
name: boxName as string,
|
||||
description: boxDescription as string
|
||||
};
|
||||
|
||||
const isEdit = mode === 'edit';
|
||||
|
||||
try {
|
||||
const response = await createBox(boxData);
|
||||
if (response.success) {
|
||||
if (response.data != null) {
|
||||
onCreateBox(response.data);
|
||||
} else {
|
||||
console.error('No data returned in the response');
|
||||
}
|
||||
} else {
|
||||
console.log(response.error || 'Unknown error');
|
||||
const response = isEdit ? await updateBox(initialData?._id!, boxData) : await createBox(boxData);
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
console.error('Box operation failed:', response.error ?? 'Unknown error');
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmitBox(response.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
finally {
|
||||
console.error(`Error during ${isEdit ? 'update' : 'create'}:`, err);
|
||||
} finally {
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -48,18 +46,16 @@ export const BoxFormModal: React.FC<BoxFormModalProps> = ({ isOpen, onClose, onC
|
||||
<input value="" id='id' name='id' readOnly={true} hidden={true} />
|
||||
<h1>Edit Box</h1>
|
||||
<fieldset>
|
||||
<label className='input' htmlFor="box-name">name</label>
|
||||
<input className='input' id='box-name' name='name' type='text' />
|
||||
<label className='input' htmlFor="box-description">description</label>
|
||||
<input className='input' id='box-description' name='description' type='text' />
|
||||
<div className="modal-actions">
|
||||
<button className='button' type="submit">Create</button>
|
||||
<button className='button' type="button" onClick={onClose}>Cancel</button>
|
||||
<label htmlFor="box-name">name</label>
|
||||
<input value={boxName} onChange={(e) => setBoxName(e.target.value)} id='box-name' name='name' type='text' />
|
||||
<label htmlFor="box-description">description</label>
|
||||
<input value={boxDescription} onChange={(e) => setBoxDescription(e.target.value)} id='box-description' name='description' type='text' />
|
||||
<div>
|
||||
<button type="submit">Submit</button>
|
||||
<button type="button" onClick={onClose}>Cancel</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
@@ -27,6 +27,38 @@ const createBox = async (boxData: Box): Promise<ApiResponse<Box>> => {
|
||||
};
|
||||
}
|
||||
}
|
||||
const updateBox = async (boxId: string, boxData: Box): Promise<ApiResponse<Box>> => {
|
||||
try {
|
||||
console.log(boxData);
|
||||
const response = await api.put<Box>(`/boxes/${boxId}`, boxData);
|
||||
return {
|
||||
success: true,
|
||||
data: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error updating box:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Could not update box',
|
||||
};
|
||||
}
|
||||
}
|
||||
const deleteBox = async (id: string): Promise<ApiResponse<null>> => {
|
||||
try {
|
||||
const response = await api.delete<null>(`boxes/${id}`);
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error deleting box:', error);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: 'Could not delete box'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export { getAllBoxes, createBox }
|
||||
export { getAllBoxes, createBox, updateBox, deleteBox }
|
||||
@@ -1,20 +1,43 @@
|
||||
import { Item } from '../item.types';
|
||||
import { useState } from 'react';
|
||||
import { Item, ItemFormModal, deleteItem } from '../';
|
||||
|
||||
interface ItemCardProps {
|
||||
item: Item;
|
||||
boxId: string;
|
||||
initialItem: Item;
|
||||
onDelete: (id: string) => void;
|
||||
}
|
||||
|
||||
export const ItemCard: React.FC<ItemCardProps> = ({ item }) => {
|
||||
export const ItemCard: React.FC<ItemCardProps> = ({ boxId, initialItem, onDelete }) => {
|
||||
const [isItemFormModalOpen, setIsItemFormModalOpen] = useState(false);
|
||||
const [item, setItem] = useState(initialItem);
|
||||
|
||||
|
||||
const handleCloseItemFormModal = () => {
|
||||
setIsItemFormModalOpen(false);
|
||||
}
|
||||
const handleClickEditItem = async () => {
|
||||
setIsItemFormModalOpen(true);
|
||||
}
|
||||
const handleUpdateItem = (updatedItem: Item) => {
|
||||
setItem(updatedItem);
|
||||
}
|
||||
|
||||
const handleDeleteItem = async () => {
|
||||
if (item._id != null) {
|
||||
const response = await deleteItem(boxId, item._id);
|
||||
if (response.success) {
|
||||
onDelete(item._id);
|
||||
} else {
|
||||
console.error('No data returned in the response');
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<li className='item'>
|
||||
<div className='item-menu-more-container'></div>
|
||||
<span className='item-name'>{item.name}</span>
|
||||
<span className='menu-btn item material-icons'>
|
||||
remove_red_eye
|
||||
</span>
|
||||
<span className='menu-btn item material-icons'>
|
||||
more_vert
|
||||
</span>
|
||||
<li>
|
||||
<ItemFormModal mode="edit" isOpen={isItemFormModalOpen} onSubmitItem={handleUpdateItem} onClose={handleCloseItemFormModal} boxId={boxId} initialData={item} />
|
||||
<span>{item.name}</span>
|
||||
<button onClick={handleClickEditItem} >edit</button>
|
||||
<button onClick={handleDeleteItem} >remove</button>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,38 +1,44 @@
|
||||
import { useState } from 'react';
|
||||
import Modal from '../../../modal';
|
||||
import { Item, createItem } from '..';
|
||||
import { Item, createItem, updateItem } from '..';
|
||||
import { createBox } from '../../box';
|
||||
|
||||
interface ItemFormModalProps {
|
||||
mode: string;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onCreateItem: (item: Item) => void;
|
||||
boxId?: string;
|
||||
onSubmitItem: (item: Item) => void;
|
||||
boxId: string;
|
||||
initialData?: Partial<Item>;
|
||||
}
|
||||
export const ItemFormModal: React.FC<ItemFormModalProps> = ({ isOpen, onClose, onCreateItem, boxId, initialData }) => {
|
||||
export const ItemFormModal: React.FC<ItemFormModalProps> = ({ mode, initialData, isOpen, onClose, onSubmitItem, boxId }) => {
|
||||
const [itemName, setItemName] = useState(initialData?.name ?? '');
|
||||
const [itemDescription, setItemDescription] = useState(initialData?.description ?? '');
|
||||
const [itemQuantity, setItemQuantity] = useState(initialData?.quantity ?? 0);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
const isEdit = mode === 'edit';
|
||||
|
||||
const itemData: Item = {
|
||||
name: formData.get('name') as string,
|
||||
description: formData.get('description') as string,
|
||||
quantity: parseInt(formData.get('quantity') as string, 10) as number,
|
||||
name: itemName as string,
|
||||
description: itemDescription as string,
|
||||
quantity: itemQuantity as number,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await createItem(itemData, boxId);
|
||||
if (response.success) {
|
||||
onCreateItem(response.data as Item);
|
||||
} else {
|
||||
console.error(response.error);
|
||||
const response = isEdit ? await updateItem(boxId!,initialData?._id!, itemData) : await createItem(itemData, boxId);
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
console.error('Item operation failed:', response.error ?? 'Unknown error');
|
||||
return;
|
||||
}
|
||||
|
||||
onSubmitItem(response.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
finally {
|
||||
console.error(`Error during ${isEdit ? 'update' : 'create'}:`, err);
|
||||
} finally {
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -45,18 +51,18 @@ export const ItemFormModal: React.FC<ItemFormModalProps> = ({ isOpen, onClose, o
|
||||
<h1>Edit Item</h1>
|
||||
<fieldset>
|
||||
|
||||
<label className='input' htmlFor="item-name">name</label>
|
||||
<input className='input' id='item-name' name='name' type='text' />
|
||||
<label htmlFor="item-name">name</label>
|
||||
<input value={itemName} onChange={(e) => setItemName(e.currentTarget.value)} id='item-name' name='name' type='text' />
|
||||
|
||||
<label className='input' htmlFor="item-description">description</label>
|
||||
<input className='input' id='item-description' name='description' type='text' />
|
||||
<label htmlFor="item-description">description</label>
|
||||
<input value={itemDescription} onChange={(e) => setItemDescription(e.currentTarget.value)}id='item-description' name='description' type='text' />
|
||||
|
||||
<label className='input' htmlFor="item-quantity">quantity</label>
|
||||
<input className='input' id='item-quantity' name='quantity' type='number' />
|
||||
<label htmlFor="item-quantity">quantity</label>
|
||||
<input value={itemQuantity} onChange={(e) => setItemQuantity(parseInt(e.currentTarget.value, 10))}id='item-quantity' name='quantity' type='number' />
|
||||
|
||||
<div className="modal-actions">
|
||||
<button className='button' type="submit">Create</button>
|
||||
<button className='button' type="button" onClick={onClose}>Cancel</button>
|
||||
<div>
|
||||
<button type="submit">Submit</button>
|
||||
<button type="button" onClick={onClose}>Cancel</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { api, ApiResponse } from '../../../api';
|
||||
import { Item } from '..';
|
||||
|
||||
const createItem = async (itemData: Item, boxId?: string,): Promise<ApiResponse<Item | null>> => {
|
||||
const createItem = async (itemData: Item, boxId: string,): Promise<ApiResponse<Item>> => {
|
||||
try {
|
||||
const response = await api.post<Item>(`/boxes/${boxId}/items`, itemData);
|
||||
return {
|
||||
@@ -12,10 +12,40 @@ const createItem = async (itemData: Item, boxId?: string,): Promise<ApiResponse<
|
||||
console.error('Error creating item:', error);
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: 'Could not create item',
|
||||
};
|
||||
}
|
||||
}
|
||||
const updateItem = async (boxId: string, itemId: string, itemData: Item): Promise<ApiResponse<Item>> => {
|
||||
try {
|
||||
const response = await api.put<Item>(`/boxes/${boxId}/items/${itemId}`, itemData);
|
||||
return {
|
||||
success: true,
|
||||
data: response.data,
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error updating item:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Could not update item',
|
||||
};
|
||||
}
|
||||
}
|
||||
const deleteItem = async (boxId: string, itemId: string): Promise<ApiResponse<null>> => {
|
||||
try {
|
||||
const response = await api.delete<null>(`boxes/${boxId}/items/${itemId}`);
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error deleting item:', error);
|
||||
|
||||
export { createItem }
|
||||
return {
|
||||
success: false,
|
||||
error: 'Could not delete item'
|
||||
}
|
||||
}
|
||||
}
|
||||
export { createItem, updateItem, deleteItem }
|
||||
@@ -1,3 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
@@ -11,3 +13,23 @@ code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
.box-card-list-container {
|
||||
@apply flex
|
||||
}
|
||||
.box-card-list {
|
||||
@apply flex flex-wrap space-x-4 p-0 m-0;
|
||||
}
|
||||
.box-card {
|
||||
@apply bg-white p-6 m-2 min-w-sm max-h-128 rounded-lg shadow-md hover:shadow-lg transition-all;
|
||||
}
|
||||
|
||||
|
||||
.box-card-title {
|
||||
@apply text-xl font-semibold text-gray-800 mb-2;
|
||||
}
|
||||
.box-card-description {
|
||||
@apply text-sm text-gray-600 mb-4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import './styles/modal.css';
|
||||
|
||||
interface ModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -10,9 +9,9 @@ const Modal: React.FC<ModalProps> = ({ isOpen, children }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className='modal-container'>
|
||||
<div className='modal-content'>
|
||||
<div className='modal-body'>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
|
||||
|
||||
:root {
|
||||
--black_light: #9aa0a6;
|
||||
--black_dark: #212121;
|
||||
--white_light:#fafafa;
|
||||
--white_darker: #e0e0e0;
|
||||
}
|
||||
html, body, #root {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--white_light);
|
||||
color: var(--black_dark)
|
||||
}
|
||||
.body-container {
|
||||
margin: 0 2em;
|
||||
overflow:auto;
|
||||
}
|
||||
form > fieldset > label {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
form > fieldset {
|
||||
border: none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-weight:normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.filter-container {
|
||||
margin: 1rem 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 344px;
|
||||
height: 44px;
|
||||
background-color: white;
|
||||
font-size: 1rem;
|
||||
border-radius: 2rem;
|
||||
border: 1px solid #dfe1e5;
|
||||
}
|
||||
.filter-container > span.icon {
|
||||
font-size: 1rem;
|
||||
margin: 0 0.75rem 0 1rem;
|
||||
color: var(--black_light);
|
||||
}
|
||||
.filter-container > span.icon.clear {
|
||||
margin-left: auto;
|
||||
}
|
||||
.filter-container > span.icon.clear:hover {
|
||||
cursor: pointer;
|
||||
color: var(--black_dark);
|
||||
}
|
||||
.filter {
|
||||
width: 100%;
|
||||
font-weight: lighter;
|
||||
border: none;
|
||||
height: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.filter:focus {
|
||||
outline: none;
|
||||
}
|
||||
.filter::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.box-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.box-menu-container {
|
||||
margin-right: 1em;
|
||||
margin-left: auto;
|
||||
}
|
||||
.box-title {
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
.box-subtitle {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
opacity: 0.6;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
ul.list-box {
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin: 0;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
li.box {
|
||||
position: relative;
|
||||
height: min-content;
|
||||
width: auto;
|
||||
padding: 16px 0;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--white_darker);
|
||||
box-shadow:
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.2),
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.14),
|
||||
0px 0px 0px 0px rgba(0,0,0,.12);
|
||||
list-style: none;
|
||||
|
||||
}
|
||||
li.box.target:hover {
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 3px 6px rgba(0,0,0,0.16),
|
||||
0 3px 6px rgba(0,0,0,0.23);
|
||||
}
|
||||
.item-menu-more-container {
|
||||
z-index: 1;
|
||||
}
|
||||
.item-menu-more-list {
|
||||
padding: 0;
|
||||
width: 120px;
|
||||
position: absolute;
|
||||
margin: 0 10px 0 0;
|
||||
top: 2rem;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--white_darker);
|
||||
box-shadow:
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.2),
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.14),
|
||||
0px 0px 0px 0px rgba(0,0,0,.12);
|
||||
}
|
||||
.box-menu-action {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-menu-action:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.item-menu-action > .icon {
|
||||
margin: 1rem 1rem;
|
||||
}
|
||||
.list-box-item {
|
||||
padding: 0;
|
||||
padding: 1rem 0 3rem 0;
|
||||
max-height: 8rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.list-box-item.empty {
|
||||
padding: 0;
|
||||
}
|
||||
.scroll-container::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
.scroll-container::-webkit-scrollbar-track {
|
||||
background: #ddd;
|
||||
}
|
||||
.scroll-container::-webkit-scrollbar-thumb {
|
||||
background: #666;
|
||||
}
|
||||
li.item {
|
||||
font-size: 0.75rem;
|
||||
display: grid;
|
||||
grid-template-columns: 0 1fr auto auto ;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 0.75rem 0.5rem 1.25rem;
|
||||
list-style: none;
|
||||
}
|
||||
.head-divider {
|
||||
margin: 16px;
|
||||
width: auto;
|
||||
height: 1px;
|
||||
background-color: var(--white_darker);
|
||||
}
|
||||
.menu-more-container {
|
||||
z-index: 1;
|
||||
}
|
||||
.menu-bottom {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.add {
|
||||
background-color: var(--white_light);
|
||||
padding: 0.75rem;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
border-radius: 50%;
|
||||
|
||||
}
|
||||
.add:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--white_darker);
|
||||
}
|
||||
.menu-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.1rem;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.menu-btn.box {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.menu-btn.item {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.menu-btn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-more-list {
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(--white_darker);
|
||||
box-shadow:
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.2),
|
||||
0px 0px 0px 0px rgba(0, 0, 0, 0.14),
|
||||
0px 0px 0px 0px rgba(0,0,0,.12);
|
||||
}
|
||||
.menu-more-list.box {
|
||||
width: 120px;
|
||||
margin: 0 10px 0 0;
|
||||
top: 4rem;
|
||||
right: 0;
|
||||
}
|
||||
.menu-more-list.item {
|
||||
width: 4.8rem;
|
||||
margin: 0 10px 0 0;
|
||||
top: 2rem;
|
||||
right: 0;
|
||||
}
|
||||
.menu-more-list.item > .menu-action > .icon {
|
||||
margin: 0.2rem 0.2rem;
|
||||
}
|
||||
.menu-action {
|
||||
z-index: inherit;
|
||||
list-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
.menu-action:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--white_darker);
|
||||
}
|
||||
.menu-action > .icon {
|
||||
margin: 0.6rem 0.6rem;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
button.button {
|
||||
height: 2.5rem;
|
||||
display: flex;
|
||||
font-size: .875rem;
|
||||
text-transform: uppercase;
|
||||
padding: 0.75rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 64px;
|
||||
background-color: var(--white_light);
|
||||
border: 1px solid var(--black_light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
button.button:hover {
|
||||
background-color: var(--white_darker);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
label.input {
|
||||
display: block;
|
||||
margin: 0 0 0.4rem 0;
|
||||
}
|
||||
input.input {
|
||||
border: 1px solid grey;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
margin: 0 0 0.8rem 0;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
.modal-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.32);
|
||||
z-index: 1000;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: white;
|
||||
border-radius: 1em;
|
||||
padding: 20px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal-action {
|
||||
display: grid;
|
||||
gap: 0.2rem;
|
||||
grid-template-columns: auto auto;
|
||||
justify-content: end;
|
||||
padding: 0.2rem;
|
||||
}
|
||||
.btn {
|
||||
background-color: white;
|
||||
}
|
||||
.btn-close:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--white_darker);
|
||||
}
|
||||
|
||||
.color-selector-container {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
width: fit-content;
|
||||
gap:3px;
|
||||
}
|
||||
.color-selector-container > .color {
|
||||
border-radius: 50%;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
margin-right: 0.2rem;
|
||||
border-style:solid;
|
||||
border-color: black;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.color-selector-container > .color.selected {
|
||||
border-style: solid;
|
||||
border-color: var(--black_light);
|
||||
}
|
||||
.color-selector-container > .color:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.color-selector-container > .color > input[type=radio] {
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
label.textarea {
|
||||
display: block;
|
||||
margin: 0 0 0.4rem 0;
|
||||
}
|
||||
textarea.textarea {
|
||||
resize: none;
|
||||
width: 90%;
|
||||
height: 6em;
|
||||
border: 1px solid grey;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
margin: 0 0 0.8rem 0;
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [
|
||||
react(),
|
||||
tailwindcss(),
|
||||
],
|
||||
})
|
||||
|
||||
+100
-26
@@ -56,19 +56,60 @@ app.post('/boxes', async (request, response) => {
|
||||
try {
|
||||
const numBoxes = await Box.countDocuments();
|
||||
const newBox = await Box.create({
|
||||
...request.body,
|
||||
sort: numBoxes,
|
||||
...request.body,
|
||||
sort: numBoxes,
|
||||
});
|
||||
|
||||
|
||||
response.status(201).json(newBox);
|
||||
} catch (error) {
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error creating box:', error);
|
||||
response.status(500).json({ error: 'Failed to create box' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update box
|
||||
app.put('/boxes/:boxId', async (request, response) => {
|
||||
try {
|
||||
const { boxId } = request.params;
|
||||
const updatedBox = await Box.findByIdAndUpdate(
|
||||
boxId,
|
||||
{ ...request.body },
|
||||
{ new: true, runValidators: true }
|
||||
);
|
||||
|
||||
if (!updatedBox) {
|
||||
return response.status(404).json({ error: 'Box not found' });
|
||||
}
|
||||
|
||||
response.status(200).json(updatedBox);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error updating box:', error);
|
||||
response.status(500).json({ error: 'Failed to update box' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//create item
|
||||
// delete box
|
||||
app.delete('/boxes/:boxId', async (request, response) => {
|
||||
try {
|
||||
const { boxId } = request.params;
|
||||
const result = await Box.findByIdAndDelete(boxId);
|
||||
|
||||
if (!result) {
|
||||
return response.status(404).json({ success: false, message: 'Box not found' });
|
||||
}
|
||||
|
||||
response.status(200).json({ success: true, message: 'Box deleted', data: result });
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error deleting box:', error);
|
||||
response.status(500).json({ success: false, error: 'Failed to delete box' });
|
||||
}
|
||||
});
|
||||
|
||||
// create item
|
||||
app.post('/boxes/:boxId/items', async (request, response) => {
|
||||
try {
|
||||
const item = new Item(request.body);
|
||||
@@ -84,32 +125,65 @@ app.post('/boxes/:boxId/items', async (request, response) => {
|
||||
response.send(item);
|
||||
} catch (error) {
|
||||
console.error('Error adding item to Box:', error);
|
||||
throw error; // or handle it according to your needs
|
||||
throw error;
|
||||
}
|
||||
|
||||
});
|
||||
//edit item
|
||||
app.put('/box/:boxId/item/:itemId', async (request, response) => {
|
||||
const boxID = request.params.boxId;
|
||||
const itemID = request.params.itemId;
|
||||
await Box.updateOne({ '_id': boxID, 'item._id': itemID }, {
|
||||
$set: {
|
||||
'item.$.name': request.body.name,
|
||||
'item.$.description': request.body.description,
|
||||
'item.$.quantity': request.body.quantity
|
||||
// edit item
|
||||
app.put('/boxes/:boxId/items/:itemId', async (request, response) => {
|
||||
const { boxId, itemId } = request.params;
|
||||
const { name, description, quantity } = request.body;
|
||||
try {
|
||||
const updatedBox = await Box.findOneAndUpdate(
|
||||
{ _id: boxId, 'items._id': itemId },
|
||||
{
|
||||
$set: {
|
||||
'items.$.name': name,
|
||||
'items.$.description': description,
|
||||
'items.$.quantity': quantity,
|
||||
},
|
||||
},
|
||||
{ new: true }
|
||||
);
|
||||
const updatedItem = updatedBox.items.find(item => item._id.toString() === itemId);
|
||||
|
||||
if (!updatedItem) {
|
||||
return response.status(404).json({ error: 'Box or item not found' });
|
||||
}
|
||||
})
|
||||
const updatedBox = await getBox(boxID);
|
||||
response.send(updatedBox);
|
||||
|
||||
response.status(200).json(updatedItem);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error updating item:', error);
|
||||
response.status(500).json({ error: 'Failed to update item' });
|
||||
}
|
||||
});
|
||||
//delete item
|
||||
app.delete('/box/:boxId/item/:itemId', async (request, response) => {
|
||||
const boxID = request.params.boxId;
|
||||
const itemID = request.params.itemId;
|
||||
await Box.update({ _id: boxID }, { $pull: { item: { _id: itemID } } });
|
||||
const box = await getBox(boxID);
|
||||
response.send(box);
|
||||
|
||||
// delete item from box
|
||||
app.delete('/boxes/:boxId/items/:itemId', async (request, response) => {
|
||||
const { boxId, itemId } = request.params;
|
||||
|
||||
try {
|
||||
const updatedBox = await Box.findByIdAndUpdate(
|
||||
boxId,
|
||||
{
|
||||
$pull: { items: { _id: itemId } }
|
||||
},
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!updatedBox) {
|
||||
return response.status(404).json({ error: 'Box not found' });
|
||||
}
|
||||
|
||||
response.json(updatedBox);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error deleting item:', error);
|
||||
response.status(500).json({ error: 'Failed to delete item' });
|
||||
}
|
||||
});
|
||||
|
||||
//move item
|
||||
app.post('/box/item/:itemId/:boxIdTarget', async (request, response) => {
|
||||
const itemID = request.params.itemId;
|
||||
|
||||
Reference in New Issue
Block a user