Compare commits
9 Commits
0fe4e22c4b
...
cbc5d32c54
| Author | SHA1 | Date | |
|---|---|---|---|
| cbc5d32c54 | |||
| a33a84825e | |||
| a6c98e42ed | |||
| cda5ea7af7 | |||
| a1e65c06c0 | |||
| c88f26cc57 | |||
| 555914b5c1 | |||
| 833a7fe51b | |||
| 23ae4c03e5 |
469
package-lock.json
generated
469
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/dom": "^1.6.12",
|
"@floating-ui/dom": "^1.6.12",
|
||||||
"pocketbase": "^0.22.1",
|
"pocketbase": "^0.22.1",
|
||||||
|
"sharp": "^0.33.5",
|
||||||
"uuid": "^11.0.3"
|
"uuid": "^11.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -115,6 +116,16 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.24.0",
|
"version": "0.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
|
||||||
@ -566,6 +577,367 @@
|
|||||||
"@typescript-eslint/typescript-estree": "^8.14.0"
|
"@typescript-eslint/typescript-estree": "^8.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-s390x": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-wasm32": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
|
||||||
|
"cpu": [
|
||||||
|
"wasm32"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/runtime": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-ia32": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-x64": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@ -1516,11 +1888,23 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/color": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-string": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
@ -1533,9 +1917,18 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
@ -1662,6 +2055,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"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/devalue": {
|
"node_modules/devalue": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
|
||||||
@ -2008,6 +2410,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/is-binary-path": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
@ -3422,9 +3830,7 @@
|
|||||||
"version": "7.6.3",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"optional": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
@ -3439,6 +3845,45 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/sharp": {
|
||||||
|
"version": "0.33.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
|
||||||
|
"integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^4.2.3",
|
||||||
|
"detect-libc": "^2.0.3",
|
||||||
|
"semver": "^7.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-darwin-arm64": "0.33.5",
|
||||||
|
"@img/sharp-darwin-x64": "0.33.5",
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.5",
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.4",
|
||||||
|
"@img/sharp-linux-arm": "0.33.5",
|
||||||
|
"@img/sharp-linux-arm64": "0.33.5",
|
||||||
|
"@img/sharp-linux-s390x": "0.33.5",
|
||||||
|
"@img/sharp-linux-x64": "0.33.5",
|
||||||
|
"@img/sharp-linuxmusl-arm64": "0.33.5",
|
||||||
|
"@img/sharp-linuxmusl-x64": "0.33.5",
|
||||||
|
"@img/sharp-wasm32": "0.33.5",
|
||||||
|
"@img/sharp-win32-ia32": "0.33.5",
|
||||||
|
"@img/sharp-win32-x64": "0.33.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -3475,6 +3920,15 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sirv": {
|
"node_modules/sirv": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz",
|
||||||
@ -3885,6 +4339,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.7.2",
|
"version": "5.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/dom": "^1.6.12",
|
"@floating-ui/dom": "^1.6.12",
|
||||||
"pocketbase": "^0.22.1",
|
"pocketbase": "^0.22.1",
|
||||||
|
"sharp": "^0.33.5",
|
||||||
"uuid": "^11.0.3"
|
"uuid": "^11.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/app.d.ts
vendored
3
src/app.d.ts
vendored
@ -1,3 +1,4 @@
|
|||||||
|
import type { User } from "$lib/schema";
|
||||||
import type { PocketBase, RecordModel } from "pocketbase";
|
import type { PocketBase, RecordModel } from "pocketbase";
|
||||||
|
|
||||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
@ -6,7 +7,7 @@ declare global {
|
|||||||
namespace App {
|
namespace App {
|
||||||
interface Locals {
|
interface Locals {
|
||||||
pb: PocketBase;
|
pb: PocketBase;
|
||||||
user: RecordModel | undefined;
|
user: User | undefined;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { Graphic, User } from "$lib/schema";
|
||||||
import type { Handle } from "@sveltejs/kit";
|
import type { Handle } from "@sveltejs/kit";
|
||||||
import PocketBase from "pocketbase";
|
import PocketBase from "pocketbase";
|
||||||
|
|
||||||
@ -14,14 +15,25 @@ export const handle: Handle = async ({ event, resolve }) => {
|
|||||||
|
|
||||||
if (event.locals.pb.authStore.isValid) {
|
if (event.locals.pb.authStore.isValid) {
|
||||||
// If the authentication data is valid, we make a "user" object easily available.
|
// If the authentication data is valid, we make a "user" object easily available.
|
||||||
event.locals.user = structuredClone(event.locals.pb.authStore.model);
|
event.locals.user = structuredClone(event.locals.pb.authStore.model) as User;
|
||||||
|
|
||||||
if (event.locals.user) {
|
if (event.locals.user) {
|
||||||
|
if (event.locals.pb.authStore.model.avatar) {
|
||||||
// Fill in the avatar URL
|
// Fill in the avatar URL
|
||||||
event.locals.user.avatar_url = event.locals.pb.files.getURL(
|
event.locals.user.avatar_url = event.locals.pb.files.getURL(
|
||||||
event.locals.pb.authStore.model,
|
event.locals.pb.authStore.model,
|
||||||
event.locals.pb.authStore.model.avatar,
|
event.locals.pb.authStore.model.avatar,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// Fill in the driver_template URL if no avatar chosen
|
||||||
|
const driver_template: Graphic = await event.locals.pb
|
||||||
|
.collection("graphics")
|
||||||
|
.getFirstListItem('name="driver_template"');
|
||||||
|
event.locals.user.avatar_url = event.locals.pb.files.getURL(
|
||||||
|
driver_template,
|
||||||
|
driver_template.file,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Set admin status for easier access
|
// Set admin status for easier access
|
||||||
event.locals.admin = event.locals.user.admin;
|
event.locals.admin = event.locals.user.admin;
|
||||||
|
|||||||
@ -69,6 +69,7 @@
|
|||||||
<input
|
<input
|
||||||
use:popup={popup_settings}
|
use:popup={popup_settings}
|
||||||
type="text"
|
type="text"
|
||||||
|
readonly
|
||||||
value={get_label(input_variable) ?? placeholder}
|
value={get_label(input_variable) ?? placeholder}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Retrieve an arbitrary object with a matching ID from an Array.
|
* Select an element from an [objects] array where [key] matches [value].
|
||||||
* Supposed to be used on collections returned by the PocketBase API.
|
* Supposed to be used on collections returned by the [PocketBase] client.
|
||||||
*/
|
*/
|
||||||
export const get_by_id = <T extends object>(objects: T[], id: string): T | undefined => {
|
export const get_by_value = <T extends object>(
|
||||||
return objects.find((o: T) => ("id" in o ? o.id === id : false));
|
objects: T[],
|
||||||
|
key: keyof T,
|
||||||
|
value: string,
|
||||||
|
): T | undefined => {
|
||||||
|
return objects.find((o: T) => (key in o ? o[key] === value : false));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,17 @@
|
|||||||
|
export interface Graphic {
|
||||||
|
name: string;
|
||||||
|
file: string;
|
||||||
|
file_url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
avatar: string;
|
||||||
|
avatar_url?: string;
|
||||||
|
admin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Team {
|
export interface Team {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@ -118,6 +118,8 @@
|
|||||||
<div class="flex flex-col gap-2 p-2">
|
<div class="flex flex-col gap-2 p-2">
|
||||||
<h4 class="h4 select-none">Enter Username and Password</h4>
|
<h4 class="h4 select-none">Enter Username and Password</h4>
|
||||||
<form method="POST" class="contents">
|
<form method="POST" class="contents">
|
||||||
|
<!-- Supply the pathname so the form can redirect to the current page. -->
|
||||||
|
<input type="hidden" name="redirect_url" value={$page.url.pathname} />
|
||||||
<Input name="username" placeholder="Username" required>
|
<Input name="username" placeholder="Username" required>
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
</Input>
|
</Input>
|
||||||
@ -144,6 +146,8 @@
|
|||||||
<div class="flex flex-col gap-2 p-2">
|
<div class="flex flex-col gap-2 p-2">
|
||||||
<h4 class="h4 select-none">Edit Profile</h4>
|
<h4 class="h4 select-none">Edit Profile</h4>
|
||||||
<form method="POST" enctype="multipart/form-data" class="contents">
|
<form method="POST" enctype="multipart/form-data" class="contents">
|
||||||
|
<!-- Supply the pathname so the form can redirect to the current page. -->
|
||||||
|
<input type="hidden" name="redirect_url" value={$page.url.pathname} />
|
||||||
<input type="hidden" name="id" value={data.user.id} />
|
<input type="hidden" name="id" value={data.user.id} />
|
||||||
<Input name="username" value={data.user.username} placeholder="Username"><UserIcon /></Input
|
<Input name="username" value={data.user.username} placeholder="Username"><UserIcon /></Input
|
||||||
>
|
>
|
||||||
@ -179,6 +183,7 @@
|
|||||||
padding="p-2"
|
padding="p-2"
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="lead">
|
<svelte:fragment slot="lead">
|
||||||
|
<div class="flex gap-2">
|
||||||
<!-- Navigation drawer -->
|
<!-- Navigation drawer -->
|
||||||
<div class="lg:hidden">
|
<div class="lg:hidden">
|
||||||
<Button color="primary" onclick={menu_drawer}>
|
<Button color="primary" onclick={menu_drawer}>
|
||||||
@ -187,7 +192,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Site logo -->
|
<!-- Site logo -->
|
||||||
<div class="ml-2 lg:ml-0">
|
|
||||||
<Button href="/" color="primary"><span class="text-xl font-bold">Formula 11</span></Button>
|
<Button href="/" color="primary"><span class="text-xl font-bold">Formula 11</span></Button>
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
@ -202,6 +206,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="trail">
|
<svelte:fragment slot="trail">
|
||||||
|
<div class="flex gap-2">
|
||||||
<!-- Data drawer -->
|
<!-- Data drawer -->
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
@ -209,19 +214,22 @@
|
|||||||
activate={$page.url.pathname.startsWith("/data")}>Data</Button
|
activate={$page.url.pathname.startsWith("/data")}>Data</Button
|
||||||
>
|
>
|
||||||
|
|
||||||
<!-- Login/Profile drawer -->
|
|
||||||
{#if !data.user}
|
{#if !data.user}
|
||||||
|
<!-- Login drawer -->
|
||||||
<Button color="primary" onclick={login_drawer}>Login</Button>
|
<Button color="primary" onclick={login_drawer}>Login</Button>
|
||||||
{:else}
|
{:else}
|
||||||
|
<!-- Profile drawer -->
|
||||||
<Avatar
|
<Avatar
|
||||||
id="user_avatar_preview"
|
id="user_avatar_preview"
|
||||||
src={data.user.avatar_url}
|
src={data.user.avatar_url}
|
||||||
rounded="rounded-full"
|
rounded="rounded-full"
|
||||||
width="w-10"
|
width="w-10"
|
||||||
|
background="bg-primary-50"
|
||||||
onclick={profile_drawer}
|
onclick={profile_drawer}
|
||||||
cursor="cursor-pointer"
|
cursor="cursor-pointer"
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Actions, PageServerLoad } from "./$types";
|
import type { Actions, PageServerLoad } from "./$types";
|
||||||
import { form_data_clean, form_data_ensure_keys, form_data_get_and_remove_id } from "$lib/form";
|
import { form_data_clean, form_data_ensure_keys, form_data_get_and_remove_id } from "$lib/form";
|
||||||
import type { Team, Driver, Race, Substitution } from "$lib/schema";
|
import type { Team, Driver, Race, Substitution, Graphic } from "$lib/schema";
|
||||||
|
|
||||||
// These "actions" run serverside only, as they're located inside +page.server.ts
|
// These "actions" run serverside only, as they're located inside +page.server.ts
|
||||||
export const actions = {
|
export const actions = {
|
||||||
@ -95,6 +95,18 @@ export const actions = {
|
|||||||
|
|
||||||
// This "load" function runs serverside only, as it's located inside +page.server.ts
|
// This "load" function runs serverside only, as it's located inside +page.server.ts
|
||||||
export const load: PageServerLoad = async ({ fetch, locals }) => {
|
export const load: PageServerLoad = async ({ fetch, locals }) => {
|
||||||
|
const fetch_graphics = async (): Promise<Graphic[]> => {
|
||||||
|
const graphics: Graphic[] = await locals.pb
|
||||||
|
.collection("graphics")
|
||||||
|
.getFullList({ fetch: fetch });
|
||||||
|
|
||||||
|
graphics.map((graphic: Graphic) => {
|
||||||
|
graphic.file_url = locals.pb.files.getURL(graphic, graphic.file);
|
||||||
|
});
|
||||||
|
|
||||||
|
return graphics;
|
||||||
|
};
|
||||||
|
|
||||||
const fetch_teams = async (): Promise<Team[]> => {
|
const fetch_teams = async (): Promise<Team[]> => {
|
||||||
const teams: Team[] = await locals.pb.collection("teams").getFullList({
|
const teams: Team[] = await locals.pb.collection("teams").getFullList({
|
||||||
sort: "+name",
|
sort: "+name",
|
||||||
@ -130,6 +142,7 @@ export const load: PageServerLoad = async ({ fetch, locals }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
graphics: await fetch_graphics(),
|
||||||
teams: await fetch_teams(),
|
teams: await fetch_teams(),
|
||||||
drivers: await fetch_drivers(),
|
drivers: await fetch_drivers(),
|
||||||
races: await fetch_races(),
|
races: await fetch_races(),
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Input, Button, Card, Search, Dropdown } from "$lib/components";
|
import { Input, Button, Card, Search, Dropdown } from "$lib/components";
|
||||||
import { get_image_preview_event_handler } from "$lib/image";
|
import { get_image_preview_event_handler } from "$lib/image";
|
||||||
import { get_by_id } from "$lib/database";
|
|
||||||
import type { Driver, Team } from "$lib/schema";
|
import type { Driver, Team } from "$lib/schema";
|
||||||
import { type PageData, type ActionData } from "./$types";
|
import { type PageData, type ActionData } from "./$types";
|
||||||
import { FileDropzone, Tab, TabGroup, type AutocompleteOption } from "@skeletonlabs/skeleton";
|
import { FileDropzone, Tab, TabGroup, type AutocompleteOption } from "@skeletonlabs/skeleton";
|
||||||
|
|
||||||
// TODO: Why does this work but import { type DropdownOption } from "$lib/components" does not?
|
// TODO: Why does this work but import { type DropdownOption } from "$lib/components" does not?
|
||||||
import type { DropdownOption } from "$lib/components/Dropdown.svelte";
|
import type { DropdownOption } from "$lib/components/Dropdown.svelte";
|
||||||
|
import { get_by_value } from "$lib/database";
|
||||||
|
|
||||||
let { data, form }: { data: PageData; form: ActionData } = $props();
|
let { data, form }: { data: PageData; form: ActionData } = $props();
|
||||||
|
|
||||||
@ -96,11 +96,12 @@
|
|||||||
|
|
||||||
<!-- Add a new team -->
|
<!-- Add a new team -->
|
||||||
{#if data.admin}
|
{#if data.admin}
|
||||||
<Card imgsrc="" imgid="create_team_logo_preview" imghidden>
|
<Card
|
||||||
|
imgsrc={get_by_value(data.graphics, "name", "team_template")?.file_url}
|
||||||
|
imgid="create_team_logo_preview"
|
||||||
|
>
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<h4 class="h4 select-none">Add a New Team</h4>
|
|
||||||
|
|
||||||
<!-- Team name input -->
|
<!-- Team name input -->
|
||||||
<Input id="team_name_create" name="name" required>Name</Input>
|
<Input id="team_name_create" name="name" required>Name</Input>
|
||||||
|
|
||||||
@ -122,7 +123,7 @@
|
|||||||
<!-- By specifying the formaction on the button (instead of action on the form), -->
|
<!-- By specifying the formaction on the button (instead of action on the form), -->
|
||||||
<!-- we can have multiple buttons with different actions in a single form. -->
|
<!-- we can have multiple buttons with different actions in a single form. -->
|
||||||
|
|
||||||
<Button formaction="?/create_team" color="secondary" submit>Create</Button>
|
<Button formaction="?/create_team" color="tertiary" submit>Create Team</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -172,6 +173,7 @@
|
|||||||
name="team"
|
name="team"
|
||||||
input_variable={update_driver_team_select_values[driver.id]}
|
input_variable={update_driver_team_select_values[driver.id]}
|
||||||
labelwidth="120px"
|
labelwidth="120px"
|
||||||
|
disabled={!data.admin}
|
||||||
options={driver_team_select_options}>Team</Dropdown
|
options={driver_team_select_options}>Team</Dropdown
|
||||||
>
|
>
|
||||||
|
|
||||||
@ -208,31 +210,23 @@
|
|||||||
|
|
||||||
<!-- Add a new driver -->
|
<!-- Add a new driver -->
|
||||||
{#if data.admin}
|
{#if data.admin}
|
||||||
<Card imgsrc="" imgid="create_driver_headshot_preview" imghidden>
|
<Card
|
||||||
|
imgsrc={get_by_value(data.graphics, "name", "driver_template")?.file_url}
|
||||||
|
imgid="create_driver_headshot_preview"
|
||||||
|
>
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<h4 class="h4 select-none">Add a New Driver</h4>
|
|
||||||
|
|
||||||
<!-- Driver data input -->
|
<!-- Driver data input -->
|
||||||
<Input
|
<Input id="driver_first_name_create" name="firstname" labelwidth="120px" required
|
||||||
id="driver_first_name_create"
|
>First Name</Input
|
||||||
name="firstname"
|
|
||||||
labelwidth="120px"
|
|
||||||
disabled={!data.admin}
|
|
||||||
required>First Name</Input
|
|
||||||
>
|
>
|
||||||
<Input
|
<Input id="driver_last_name_create" name="lastname" labelwidth="120px" required
|
||||||
id="driver_last_name_create"
|
>Last Name</Input
|
||||||
name="lastname"
|
|
||||||
labelwidth="120px"
|
|
||||||
disabled={!data.admin}
|
|
||||||
required>Last Name</Input
|
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
id="driver_code_create"
|
id="driver_code_create"
|
||||||
name="code"
|
name="code"
|
||||||
labelwidth="120px"
|
labelwidth="120px"
|
||||||
disabled={!data.admin}
|
|
||||||
maxlength={3}
|
maxlength={3}
|
||||||
minlength={3}
|
minlength={3}
|
||||||
required>Driver Code</Input
|
required>Driver Code</Input
|
||||||
@ -252,7 +246,6 @@
|
|||||||
name="headshot"
|
name="headshot"
|
||||||
id="driver_headshot_create"
|
id="driver_headshot_create"
|
||||||
onchange={get_image_preview_event_handler("create_driver_headshot_preview")}
|
onchange={get_image_preview_event_handler("create_driver_headshot_preview")}
|
||||||
disabled={!data.admin}
|
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="message"
|
<svelte:fragment slot="message"
|
||||||
@ -262,7 +255,9 @@
|
|||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<Button formaction="?/create_driver" color="secondary" submit>Create</Button>
|
<Button formaction="?/create_driver" color="secondary" submit
|
||||||
|
>Create Driver</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import { form_data_clean, form_data_ensure_keys, form_data_get_and_remove_id } from "$lib/form";
|
import { form_data_clean, form_data_ensure_keys, form_data_get_and_remove_id } from "$lib/form";
|
||||||
import { error, redirect } from "@sveltejs/kit";
|
import { error, redirect } from "@sveltejs/kit";
|
||||||
import type { Actions } from "./$types";
|
import type { Actions } from "./$types";
|
||||||
|
import type { User } from "$lib/schema";
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
create_profile: async ({ request, locals }) => {
|
create_profile: async ({ request, locals }): Promise<void> => {
|
||||||
const data = form_data_clean(await request.formData());
|
const data: FormData = form_data_clean(await request.formData());
|
||||||
form_data_ensure_keys(data, ["username", "password"]);
|
form_data_ensure_keys(data, ["username", "password", "redirect_url"]);
|
||||||
|
|
||||||
// TODO: Errrr passwordConfirm... How to integrate it into the unified login-/register-UI?
|
// Confirm password lol
|
||||||
const record = await locals.pb.collection("users").create({
|
const record = await locals.pb.collection("users").create({
|
||||||
username: data.get("username")?.toString(),
|
username: data.get("username")?.toString(),
|
||||||
password: data.get("password")?.toString(),
|
password: data.get("password")?.toString(),
|
||||||
@ -20,46 +21,50 @@ export const actions = {
|
|||||||
.collection("users")
|
.collection("users")
|
||||||
.authWithPassword(data.get("username")?.toString(), data.get("password")?.toString());
|
.authWithPassword(data.get("username")?.toString(), data.get("password")?.toString());
|
||||||
|
|
||||||
redirect(303, "/");
|
redirect(303, data.get("redirect_url")?.toString() ?? "/");
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: PocketBase API rule: Only the active user should be able to modify itself
|
// TODO: PocketBase API rule: Only the active user should be able to modify itself
|
||||||
update_profile: async ({ request, locals }) => {
|
update_profile: async ({ request, locals }): Promise<void> => {
|
||||||
const data = form_data_clean(await request.formData());
|
const data: FormData = form_data_clean(await request.formData());
|
||||||
const id = form_data_get_and_remove_id(data);
|
form_data_ensure_keys(data, ["redirect_url"]);
|
||||||
|
const id: string = form_data_get_and_remove_id(data);
|
||||||
|
|
||||||
const record = await locals.pb.collection("users").update(id, data);
|
const record: User = await locals.pb.collection("users").update(id, data);
|
||||||
|
|
||||||
redirect(303, "/");
|
redirect(303, data.get("redirect_url")?.toString() ?? "/");
|
||||||
},
|
},
|
||||||
|
|
||||||
login: async ({ request, locals }) => {
|
login: async ({ request, locals }) => {
|
||||||
if (locals.user) {
|
if (locals.user) {
|
||||||
console.log("Already logged in!");
|
error(400, "Already logged in!");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = form_data_clean(await request.formData());
|
const data: FormData = form_data_clean(await request.formData());
|
||||||
form_data_ensure_keys(data, ["username", "password"]);
|
form_data_ensure_keys(data, ["username", "password", "redirect_url"]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await locals.pb
|
await locals.pb
|
||||||
.collection("users")
|
.collection("users")
|
||||||
.authWithPassword(data.get("username")?.toString(), data.get("password")?.toString());
|
.authWithPassword(data.get("username")?.toString(), data.get("password")?.toString());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(`Failed to login: ${err}`);
|
|
||||||
error(400, "Failed to login!");
|
error(400, "Failed to login!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Would be better to redirect to previous page somehow...
|
redirect(303, data.get("redirect_url")?.toString() ?? "/");
|
||||||
redirect(303, "/");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
logout: async ({ locals }) => {
|
logout: async ({ request, locals }) => {
|
||||||
|
if (!locals.user) {
|
||||||
|
error(400, "Not logged in!");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: FormData = form_data_clean(await request.formData());
|
||||||
|
form_data_ensure_keys(data, ["redirect_url"]);
|
||||||
|
|
||||||
locals.pb.authStore.clear();
|
locals.pb.authStore.clear();
|
||||||
locals.user = undefined;
|
locals.user = undefined;
|
||||||
|
|
||||||
// TODO: Would be better to redirect to previous page somehow...
|
redirect(303, data.get("redirect_url")?.toString() ?? "/");
|
||||||
redirect(303, "/");
|
|
||||||
},
|
},
|
||||||
} satisfies Actions;
|
} satisfies Actions;
|
||||||
|
|||||||
Reference in New Issue
Block a user