Compare commits
26
Commits
3955f95830
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60ad76d48a | ||
|
|
38fe82731e | ||
|
|
5cce7928ec | ||
|
|
6f375cd990 | ||
|
|
681b15e793 | ||
|
|
53a75f6573 | ||
|
|
eb40444e46 | ||
|
|
1b179f9d16 | ||
|
|
7e6e31aa83 | ||
|
|
ee19bd9dea | ||
|
|
aa501b4c16 | ||
|
|
feb80e41ea | ||
|
|
d9607afd2c | ||
|
|
adaf9534b5 | ||
|
|
8ce4ff1210 | ||
|
|
8c26522ec2 | ||
|
|
264b44f555 | ||
|
|
bce9a19776 | ||
|
|
e6e4b068c9 | ||
|
|
9c221fa828 | ||
|
|
3925f4e67a | ||
|
|
a0efc6c628 | ||
|
|
09cdce350d | ||
|
|
81f0113ce2 | ||
|
|
1dcbca0445 | ||
|
|
2a1436515b |
@@ -12,9 +12,6 @@ jobs:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Docker
|
||||
run: curl -fsSL https://get.docker.com | sh
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# CareerConnect - FBLA 2025
|
||||
# Westuffind - FBLA 2026
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
Generated
+290
-153
@@ -8,6 +8,7 @@
|
||||
"name": "fbla26",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@rive-app/canvas": "^2.37.1",
|
||||
"bcrypt": "^6.0.0",
|
||||
"desm": "^1.3.1",
|
||||
"dotenv": "^17.2.3",
|
||||
@@ -24,7 +25,7 @@
|
||||
"@eslint/compat": "^1.4.0",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@internationalized/date": "^3.10.1",
|
||||
"@lucide/svelte": "^0.561.0",
|
||||
"@lucide/svelte": "^1.7.0",
|
||||
"@sveltejs/adapter-node": "^5.3.2",
|
||||
"@sveltejs/kit": "2.49.5",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
@@ -47,6 +48,7 @@
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"svelte-sonner": "^1.1.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwind-variants": "^3.2.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
@@ -1337,9 +1339,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lucide/svelte": {
|
||||
"version": "0.561.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.561.0.tgz",
|
||||
"integrity": "sha512-vofKV2UFVrKE6I4ewKJ3dfCXSV6iP6nWVmiM83MLjsU91EeJcEg7LoWUABLp/aOTxj1HQNbJD1f3g3L0JQgH9A==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.7.0.tgz",
|
||||
"integrity": "sha512-YytBKOUBGox7yWcykZnYxOkn5WpR5G1qYXLYXV/j1B79SOTTEKzB+s5yF5Rq9l9OkweDStNH2b4yTqfvhEhV8g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
@@ -1391,6 +1393,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rive-app/canvas": {
|
||||
"version": "2.37.1",
|
||||
"resolved": "https://registry.npmjs.org/@rive-app/canvas/-/canvas-2.37.1.tgz",
|
||||
"integrity": "sha512-Uw14b9vXD5g56ttUktlV+mXX1EOpAwqOoMS+DAVSs6EDoeaUYM4bRSKB1glDiOotqjshLSfNihua73mLPp10cQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
"version": "28.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz",
|
||||
@@ -1488,9 +1496,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
|
||||
"integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
|
||||
"integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1502,9 +1510,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
|
||||
"integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1516,9 +1524,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
|
||||
"integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1530,9 +1538,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
|
||||
"integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
|
||||
"integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1544,9 +1552,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
|
||||
"integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1558,9 +1566,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
|
||||
"integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
|
||||
"integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1572,13 +1580,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
|
||||
"integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
|
||||
"integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1586,13 +1597,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
|
||||
"integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
|
||||
"integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1600,13 +1614,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1614,13 +1631,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
|
||||
"integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1628,13 +1648,33 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
|
||||
"integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1642,13 +1682,33 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
|
||||
"integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1656,13 +1716,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1670,13 +1733,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
|
||||
"integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1684,13 +1750,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1698,13 +1767,16 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -1712,9 +1784,26 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz",
|
||||
"integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
|
||||
"integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
|
||||
"integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1722,13 +1811,13 @@
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
"openbsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
|
||||
"integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
|
||||
"integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1740,9 +1829,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
|
||||
"integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1754,9 +1843,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
|
||||
"integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1768,9 +1857,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
|
||||
"integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz",
|
||||
"integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1782,9 +1871,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
|
||||
"integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz",
|
||||
"integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2342,6 +2431,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz",
|
||||
@@ -2493,7 +2588,7 @@
|
||||
"version": "8.46.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz",
|
||||
"integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2533,9 +2628,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2543,13 +2638,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
@@ -2797,9 +2892,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2946,9 +3041,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3294,10 +3389,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/devalue": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
|
||||
"integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
|
||||
"dev": true,
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz",
|
||||
"integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-accessibility-api": {
|
||||
@@ -3648,12 +3742,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/esrap": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz",
|
||||
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz",
|
||||
"integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/types": "^8.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@typescript-eslint/types": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/esrecurse": {
|
||||
@@ -3925,9 +4027,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -4287,9 +4389,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4805,9 +4907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4853,9 +4955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -5174,9 +5276,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
||||
"integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -5208,9 +5310,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5312,9 +5414,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
||||
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
@@ -5578,9 +5680,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||
"version": "6.15.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
|
||||
"integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
@@ -5701,9 +5803,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.52.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
|
||||
"integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
|
||||
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5717,28 +5819,31 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.52.4",
|
||||
"@rollup/rollup-android-arm64": "4.52.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.52.4",
|
||||
"@rollup/rollup-darwin-x64": "4.52.4",
|
||||
"@rollup/rollup-freebsd-arm64": "4.52.4",
|
||||
"@rollup/rollup-freebsd-x64": "4.52.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.52.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.52.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.52.4",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.52.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.52.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.52.4",
|
||||
"@rollup/rollup-openharmony-arm64": "4.52.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.52.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.52.4",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.52.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.52.4",
|
||||
"@rollup/rollup-android-arm-eabi": "4.60.1",
|
||||
"@rollup/rollup-android-arm64": "4.60.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.60.1",
|
||||
"@rollup/rollup-darwin-x64": "4.60.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.60.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.60.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.60.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.60.1",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.60.1",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.60.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.60.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.60.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.60.1",
|
||||
"@rollup/rollup-openbsd-x64": "4.60.1",
|
||||
"@rollup/rollup-openharmony-arm64": "4.60.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.60.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.60.1",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.60.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.60.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -6179,21 +6284,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.39.11",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.39.11.tgz",
|
||||
"integrity": "sha512-8MxWVm2+3YwrFbPaxOlT1bbMi6OTenrAgks6soZfiaS8Fptk4EVyRIFhJc3RpO264EeSNwgjWAdki0ufg4zkGw==",
|
||||
"version": "5.55.3",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.3.tgz",
|
||||
"integrity": "sha512-dS1N+i3bA1v+c4UDb750MlN5vCO82G6vxh8HeTsPsTdJ1BLsN1zxSyDlIdBBqUjqZ/BxEwM8UrFf98aaoVnZFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@sveltejs/acorn-typescript": "^1.0.5",
|
||||
"@types/estree": "^1.0.5",
|
||||
"@types/trusted-types": "^2.0.7",
|
||||
"acorn": "^8.12.1",
|
||||
"aria-query": "^5.3.1",
|
||||
"aria-query": "5.3.1",
|
||||
"axobject-query": "^4.1.0",
|
||||
"clsx": "^2.1.1",
|
||||
"devalue": "^5.6.4",
|
||||
"esm-env": "^1.2.1",
|
||||
"esrap": "^2.1.0",
|
||||
"esrap": "^2.2.4",
|
||||
"is-reference": "^3.0.3",
|
||||
"locate-character": "^3.0.0",
|
||||
"magic-string": "^0.30.11",
|
||||
@@ -6311,6 +6418,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-sonner": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.1.0.tgz",
|
||||
"integrity": "sha512-3lYM6ZIqWe+p9vwwWHGWP/ZdvHiUtzURsud2quIxivrX4rvpXh6i+geBGn0m3JS6KwW6W8VgbOl3xQMcDuh6gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"runed": "^0.28.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-sonner/node_modules/runed": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz",
|
||||
"integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/huntabyte",
|
||||
"https://github.com/sponsors/tglide"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esm-env": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-toolbelt": {
|
||||
"version": "0.10.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz",
|
||||
@@ -6333,9 +6470,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte/node_modules/aria-query": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
|
||||
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
|
||||
"integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -6410,11 +6547,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz",
|
||||
"integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
|
||||
"version": "7.5.13",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz",
|
||||
"integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
"chownr": "^3.0.0",
|
||||
|
||||
+4
-2
@@ -19,7 +19,7 @@
|
||||
"@eslint/compat": "^1.4.0",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@internationalized/date": "^3.10.1",
|
||||
"@lucide/svelte": "^0.561.0",
|
||||
"@lucide/svelte": "^1.7.0",
|
||||
"@sveltejs/adapter-node": "^5.3.2",
|
||||
"@sveltejs/kit": "2.49.5",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
@@ -42,6 +42,7 @@
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"svelte-sonner": "^1.1.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwind-variants": "^3.2.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
@@ -63,6 +64,7 @@
|
||||
"postgres": "^3.4.7",
|
||||
"sharp": "^0.34.5",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"valibot": "^1.2.0"
|
||||
"valibot": "^1.2.0",
|
||||
"@rive-app/canvas": "^2.37.1"
|
||||
}
|
||||
}
|
||||
|
||||
+77
-9
@@ -8,20 +8,22 @@
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card: oklch(0.95 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.5852 0.25 292.717);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--dull-primary: oklch(0.8225 0.0927 293.01);
|
||||
--dull-primary-foreground: oklch(0.0 0.016 293.756);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--muted-foreground: oklch(0.502 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--border: oklch(0.9 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.606 0.25 292.717);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
@@ -37,10 +39,10 @@
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.606 0.25 292.717);
|
||||
--warning: oklch(0.84 0.16 84);
|
||||
--warning: oklch(0.6 0.16 84);
|
||||
--error: oklch(0.577 0.245 27.325);
|
||||
--positive: oklch(0.5 0.2067 147.18);
|
||||
--action: oklch(0.5852 0.2263 260.47);
|
||||
--action: oklch(0.4852 0.2263 260.47);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -50,12 +52,15 @@
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.541 0.281 293.009);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--primary: oklch(0.5 0.331 293.009);
|
||||
--primary-foreground: oklch(0.0 0.016 293.756);
|
||||
--dull-primary: oklch(0.3 0.281 293.009);
|
||||
--dull-primary-foreground: oklch(0.969 0.016 293.756);
|
||||
/*--primary-foreground: oklch(0.969 0.016 293.756);*/
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--muted-foreground: oklch(0.655 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
@@ -78,7 +83,7 @@
|
||||
--warning: oklch(0.84 0.16 84);
|
||||
--error: oklch(0.704 0.191 22.216);
|
||||
--positive: oklch(0.7522 0.2067 147.18);
|
||||
--action: oklch(0.6098 0.1872 260.47);
|
||||
--action: oklch(0.7098 0.1872 260.47);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
@@ -94,6 +99,8 @@
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-dull-primary: var(--dull-primary);
|
||||
--color-dull-primary-foreground: var(--dull-primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
@@ -133,3 +140,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-open {
|
||||
&:where([data-state="open"]), &:where([data-open]:not([data-open="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-closed {
|
||||
&:where([data-state="closed"]), &:where([data-closed]:not([data-closed="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-checked {
|
||||
&:where([data-state="checked"]), &:where([data-checked]:not([data-checked="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-unchecked {
|
||||
&:where([data-state="unchecked"]), &:where([data-unchecked]:not([data-unchecked="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-selected {
|
||||
&:where([data-selected]) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-disabled {
|
||||
&:where([data-disabled="true"]), &:where([data-disabled]:not([data-disabled="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-active {
|
||||
&:where([data-state="active"]), &:where([data-active]:not([data-active="false"])) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-horizontal {
|
||||
&:where([data-orientation="horizontal"]) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@custom-variant data-vertical {
|
||||
&:where([data-orientation="vertical"]) {
|
||||
@slot;
|
||||
}
|
||||
}
|
||||
|
||||
@utility no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { User, UserPayload } from '$lib/types/user';
|
||||
import { DefaultUserSettings, type User, type UserPayload } from '$lib/types/user';
|
||||
import bcrypt from 'bcrypt';
|
||||
import sql from '$lib/db/db.server';
|
||||
import { type Cookies, error } from '@sveltejs/kit';
|
||||
@@ -9,7 +9,10 @@ export function setJWTCookie(cookies: Cookies, user: User) {
|
||||
const payload = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name
|
||||
name: user.name,
|
||||
settings: user.settings || DefaultUserSettings,
|
||||
createdAt: user.createdAt,
|
||||
lastSignIn: user.lastSignIn
|
||||
};
|
||||
|
||||
if (process.env.JWT_SECRET === undefined) {
|
||||
@@ -61,12 +64,14 @@ export async function login(email: string, password: string): Promise<User> {
|
||||
SELECT * FROM users
|
||||
WHERE email = ${email};
|
||||
`;
|
||||
if (typeof user.settings == 'string') {
|
||||
user.settings = JSON.parse(user.settings);
|
||||
}
|
||||
if (user) {
|
||||
if (await bcrypt.compare(password, user.passwordHash!)) {
|
||||
delete user.passwordHash;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
sql`
|
||||
await sql`
|
||||
UPDATE users
|
||||
SET last_sign_in = NOW()
|
||||
WHERE id = ${user.id};
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Content>
|
||||
<Dialog.Content onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Claim Item</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
@@ -44,10 +44,7 @@
|
||||
<div class="inline-block">
|
||||
<div class="flex-1">{item?.description}</div>
|
||||
{#if item?.foundLocation}
|
||||
<div class="mt-2">
|
||||
<LocationIcon class="float-left mr-1" size={24} />
|
||||
<div>{item?.foundLocation}</div>
|
||||
</div>
|
||||
<span class="mt-2"><strong class="mr-1">Found at:</strong> {item.foundLocation}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -117,10 +117,13 @@
|
||||
|
||||
<Dialog.Footer class="mt-4">
|
||||
<div class="flex justify-between w-full">
|
||||
<Button variant="destructive"
|
||||
onclick={() => {approveDenyItem({id: item?.id || 0, approved: false}); invalidateAll();}}>
|
||||
<Dialog.Close
|
||||
type="button"
|
||||
class={buttonVariants({ variant: "destructive" })}
|
||||
onclick={async () => {await approveDenyItem({ id: item?.id || 0, approved: false }); await invalidateAll(); }}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Dialog.Close>
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
}
|
||||
|
||||
function handleFiles(files: FileList | null) {
|
||||
console.log('handleFiles');
|
||||
if (!files || files.length === 0) return;
|
||||
|
||||
const selected = files[0];
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Content>
|
||||
<Dialog.Content onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Item Inquiry</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
@@ -45,10 +45,7 @@
|
||||
<div class="inline-block">
|
||||
<div class="flex-1">{item?.description}</div>
|
||||
{#if item?.foundLocation}
|
||||
<div class="mt-2">
|
||||
<LocationIcon class="float-left mr-1" size={24} />
|
||||
<div>{item?.foundLocation}</div>
|
||||
</div>
|
||||
<span class="mt-2 flex"><strong class="mr-1">Found at:</strong> {item.foundLocation}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,43 +16,45 @@
|
||||
import { approveDenyItem, restoreClaimedItem } from '$lib/db/items.remote';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import NoImagePlaceholder from './no-image-placeholder.svelte';
|
||||
import type { User } from '$lib/types/user';
|
||||
|
||||
export let item: Item = <Item>{};
|
||||
export let admin = false;
|
||||
export let user: User | null = null;
|
||||
export let editCallback: (item: Item) => void;
|
||||
export let inquireCallback: (item: Item) => void;
|
||||
export let claimCallback: (item: Item) => void;
|
||||
|
||||
let timeSincePosted: number | string = (new Date().getTime() - item.foundDate.getTime()) / 1000 / 60 / 60 / 24; // days
|
||||
if (timeSincePosted < 1) {
|
||||
timeSincePosted = '<1';
|
||||
} else {
|
||||
timeSincePosted = Math.round(timeSincePosted);
|
||||
}
|
||||
// Normalize the foundDate in case it was serialized (string) in prod builds
|
||||
// (SSR/JSON serialization often turns Date objects into strings).
|
||||
const foundDateObj: Date = item?.foundDate instanceof Date
|
||||
? item.foundDate
|
||||
: new Date(item?.foundDate);
|
||||
|
||||
// Compute days since posted as a number (always a number, fall back to 0 on invalid date)
|
||||
let timeSincePosted: number = (() => {
|
||||
const t = foundDateObj?.getTime?.();
|
||||
if (!t || Number.isNaN(t)) return 0;
|
||||
return (Date.now() - t) / 1000 / 60 / 60 / 24; // days (float)
|
||||
})();
|
||||
// If you want to round/display differently you can do that in the template.
|
||||
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="h-full bg-card text-card-foreground flex flex-col gap-2 rounded-xl border shadow-sm max-w-sm overflow-hidden min-w-2xs">
|
||||
class="text-left h-full bg-card text-card-foreground flex flex-col gap-2 rounded-xl border shadow-sm max-w-sm overflow-hidden min-w-2xs" onclick={() => {claimCallback(item)}}>
|
||||
{#if item.image}
|
||||
<img src="https://fbla26.marinodev.com/uploads/{item.id}.jpg" class="object-cover min-h-48 max-h-48"
|
||||
<img src="https://fbla26.marinodev.com/uploads/{item.id}.jpg" class="object-cover min-h-56 max-h-56"
|
||||
alt="Lost item">
|
||||
{:else}
|
||||
<div class="min-h-48 w-full bg-accent flex flex-col justify-center">
|
||||
|
||||
<div class="justify-center flex ">
|
||||
|
||||
<NoImagePlaceholder className="" />
|
||||
</div>
|
||||
<p class="text-center mt-4">No image available</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex-col flex h-full px-2 pb-2">
|
||||
|
||||
<!-- <div class="font-bold inline-block">{item.title}</div>-->
|
||||
<!-- <div class="inline-block">-->
|
||||
<div>
|
||||
|
||||
{#if item.transferred}
|
||||
<Badge variant="secondary" class="inline-block">In Lost & Found</Badge>
|
||||
{:else}
|
||||
@@ -62,60 +64,68 @@
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger
|
||||
>
|
||||
<Badge variant="outline" class="inline-block">{timeSincePosted}
|
||||
day{(timeSincePosted === 1 || timeSincePosted === '<1') ? '' : 's'} ago
|
||||
<Badge variant="outline"
|
||||
class="inline-block {user?.settings?.staleItemDays !== undefined && timeSincePosted >= user.settings.staleItemDays ? 'text-warning' : ''}">{timeSincePosted < 1 ? "<1" : Math.round(timeSincePosted)}
|
||||
day{(timeSincePosted <= 1) ? '' : 's'} ago
|
||||
</Badge>
|
||||
</Tooltip.Trigger
|
||||
>
|
||||
<Tooltip.Content>
|
||||
<p>{item.foundDate.toLocaleDateString('en-US', dateFormatOptions)}</p>
|
||||
<p>{foundDateObj.toLocaleDateString('en-US', dateFormatOptions)}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex-1">{item.description}</div>
|
||||
{#if item.foundLocation}
|
||||
<div class="mt-2">
|
||||
<LocationIcon class="float-left mr-1" size={24} />
|
||||
<div>{item.foundLocation}</div>
|
||||
<div>
|
||||
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<span class="mt-2 flex"><strong class="mr-1">Found at:</strong> {item.foundLocation}</span>
|
||||
</Tooltip.Trigger
|
||||
>
|
||||
<Tooltip.Content>
|
||||
<p>Item was found here</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if admin}
|
||||
{#if user !== null}
|
||||
<div class="mt-2 justify-between flex">
|
||||
{#if item.approvedDate === null}
|
||||
|
||||
<Button variant="ghost" class="text-positive"
|
||||
onclick={async () => {await approveDenyItem({id: item.id, approved: true});
|
||||
invalidateAll()}}>
|
||||
onclick={async (e) => {e.stopPropagation(); await approveDenyItem({id: item.id, approved: true});
|
||||
await invalidateAll()}}>
|
||||
<CheckIcon />
|
||||
Approve
|
||||
</Button>
|
||||
<Button variant="ghost" class="text-destructive"
|
||||
onclick={async () => {await approveDenyItem({id: item.id, approved: false});
|
||||
invalidateAll()}}>
|
||||
onclick={async (e) => {e.stopPropagation(); await approveDenyItem({id: item.id, approved: false});
|
||||
await invalidateAll()}}>
|
||||
<XIcon />
|
||||
Deny
|
||||
</Button>
|
||||
{/if}
|
||||
{#if item.claimedDate === null}
|
||||
<Button variant="ghost" class="text-action"
|
||||
onclick={() => {editCallback(item)}}>
|
||||
onclick={(e) => {e.stopPropagation(); editCallback(item)}}>
|
||||
<PencilIcon />
|
||||
Manage
|
||||
{!item.approvedDate ? 'Edit' : 'Manage'}
|
||||
</Button>
|
||||
{:else}
|
||||
<Button variant="ghost" class="text-destructive"
|
||||
onclick={async () => {await approveDenyItem({id: item.id, approved: false});
|
||||
invalidateAll()}}>
|
||||
onclick={async (e) => {e.stopPropagation(); await approveDenyItem({id: item.id, approved: false});
|
||||
await invalidateAll()}}>
|
||||
<TrashIcon />
|
||||
Delete
|
||||
</Button>
|
||||
<Button variant="ghost" class="text-action"
|
||||
onclick={async () => {await restoreClaimedItem(item.id);
|
||||
invalidateAll()}}>
|
||||
onclick={async (e) => {e.stopPropagation(); await restoreClaimedItem(item.id);
|
||||
await invalidateAll()}}>
|
||||
<ArchiveRestoreIcon />
|
||||
Restore
|
||||
</Button>
|
||||
@@ -124,13 +134,13 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mt-2 justify-between flex">
|
||||
<Button variant="ghost" class="text-action"
|
||||
onclick={() => {inquireCallback(item)}}>
|
||||
<Button variant="ghost" class="text-action z-10"
|
||||
onclick={(e) => {e.stopPropagation(); inquireCallback(item)}}>
|
||||
<NotebookPenIcon />
|
||||
Inquire
|
||||
</Button>
|
||||
<Button variant="ghost" class="text-primary"
|
||||
onclick={() => {claimCallback(item)}}>
|
||||
<Button variant="ghost" class="text-primary z-10"
|
||||
onclick={(e) => {e.stopPropagation(); claimCallback(item)}}>
|
||||
<StarIcon />
|
||||
Claim
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
|
||||
export let src = '/mdev_triangle_loading.riv';
|
||||
export let stateMachine = null;
|
||||
export let size = 120;
|
||||
export let classes = '';
|
||||
|
||||
|
||||
let canvas;
|
||||
let riveInstance;
|
||||
|
||||
onMount(async () => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
const mod = await import('@rive-app/canvas');
|
||||
|
||||
// Handle BOTH CJS and ESM shapes safely
|
||||
const Rive = mod?.Rive || mod?.default?.Rive || mod?.default;
|
||||
|
||||
if (!Rive) {
|
||||
console.error('Rive failed to load:', mod);
|
||||
return;
|
||||
}
|
||||
|
||||
riveInstance = new Rive({
|
||||
src,
|
||||
canvas,
|
||||
autoplay: true,
|
||||
// stateMachines: stateMachine || undefined,
|
||||
onLoad: () => {
|
||||
riveInstance.resizeDrawingSurfaceToCanvas();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
riveInstance?.cleanup?.();
|
||||
});
|
||||
</script>
|
||||
|
||||
<canvas bind:this={canvas} class={classes} />
|
||||
@@ -6,7 +6,7 @@
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Field from '$lib/components/ui/field';
|
||||
import { fileToBase64 } from '$lib/shared';
|
||||
|
||||
import LoadingSpinner from './loading-spinner.svelte';
|
||||
import ImageUpload from '$lib/components/custom/image-upload/image-upload.svelte';
|
||||
import { genDescription } from '$lib/db/items.remote';
|
||||
import { EMAIL_REGEX_STRING } from '$lib/consts';
|
||||
@@ -17,12 +17,47 @@
|
||||
let description: string | undefined = $state();
|
||||
let isGenerating = $state(false);
|
||||
|
||||
async function resizeImage(
|
||||
file: File,
|
||||
maxWidth = 300,
|
||||
maxHeight = 300,
|
||||
quality = 0.8
|
||||
): Promise<File> {
|
||||
const bitmap = await createImageBitmap(file);
|
||||
|
||||
let { width, height } = bitmap;
|
||||
|
||||
const scale = Math.min(maxWidth / width, maxHeight / height, 1);
|
||||
|
||||
width = Math.round(width * scale);
|
||||
height = Math.round(height * scale);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.drawImage(bitmap, 0, 0, width, height);
|
||||
|
||||
const blob = await new Promise<Blob>((resolve) =>
|
||||
canvas.toBlob((blob) => resolve(blob!), "image/jpeg", quality)
|
||||
);
|
||||
|
||||
return new File([blob], file.name, {
|
||||
type: "image/jpeg",
|
||||
lastModified: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
async function onSelect(file: File) {
|
||||
isGenerating = true;
|
||||
|
||||
const base64 = await fileToBase64(file);
|
||||
const resized = await resizeImage(file, 300, 300, 0.7);
|
||||
|
||||
const base64 = await fileToBase64(resized);
|
||||
|
||||
description = await genDescription(base64);
|
||||
|
||||
isGenerating = false;
|
||||
}
|
||||
|
||||
@@ -30,7 +65,7 @@
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Content>
|
||||
<Dialog.Content onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Submit Found Item</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
@@ -69,7 +104,11 @@
|
||||
/>
|
||||
</Field.Field>
|
||||
|
||||
<RadioGroup.Root name="location" bind:value={itemLocation}>
|
||||
|
||||
<RadioGroup.Root name="location" bind:value={itemLocation} required>
|
||||
<Field.Label for="location">
|
||||
Where is it? <span class="text-error">*</span>
|
||||
</Field.Label>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroup.Item value="finderPossession" id="finderPossession" />
|
||||
<Label for="finderPossession">
|
||||
@@ -115,8 +154,11 @@
|
||||
</Dialog.Root>
|
||||
|
||||
|
||||
{#if isGenerating}
|
||||
<div class="fixed inset-0 bg-black/75 z-999999 w-screen h-screen justify-center items-center flex">
|
||||
<p class="text-6xl text-primary">Loading...</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="fixed inset-0 bg-black/85 z-[999999] w-screen h-screen flex justify-center items-center transition-opacity duration-150 flex-col md:flex-row"
|
||||
class:opacity-0={!isGenerating}
|
||||
class:pointer-events-none={!isGenerating}
|
||||
>
|
||||
<LoadingSpinner classes="w-28 md:w-48 mb-2 md:-mb-4" />
|
||||
<p class="text-4xl md:text-6xl text-white">Loading...</p>
|
||||
</div>
|
||||
|
||||
@@ -1,54 +1,56 @@
|
||||
<script lang="ts" module>
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
import { cn, type WithElementRef } from '$lib/utils.js';
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
base: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs",
|
||||
default: 'bg-dull-primary text-dull-primary-foreground hover:bg-primary/90 shadow-xs',
|
||||
// dull_default: 'bg-dull-primary text-dull-primary-foreground hover:bg-dull-primary/90 shadow-xs',
|
||||
destructive:
|
||||
"bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs",
|
||||
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs',
|
||||
outline:
|
||||
"bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
},
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
|
||||
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||
xl: 'h-14 rounded-md px-8 has-[>svg]:px-6',
|
||||
icon: 'size-9',
|
||||
'icon-sm': 'size-8',
|
||||
'icon-lg': 'size-10'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
||||
|
||||
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
||||
WithElementRef<HTMLAnchorAttributes> & {
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
};
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
let {
|
||||
class: className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
ref = $bindable(null),
|
||||
href = undefined,
|
||||
type = "button",
|
||||
type = 'button',
|
||||
disabled,
|
||||
children,
|
||||
...restProps
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Toaster } from "./sonner.svelte";
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
|
||||
import { mode } from "mode-watcher";
|
||||
import Loader2Icon from '@lucide/svelte/icons/loader-2';
|
||||
import CircleCheckIcon from '@lucide/svelte/icons/circle-check';
|
||||
import OctagonXIcon from '@lucide/svelte/icons/octagon-x';
|
||||
import InfoIcon from '@lucide/svelte/icons/info';
|
||||
import TriangleAlertIcon from '@lucide/svelte/icons/triangle-alert';
|
||||
|
||||
let { ...restProps }: SonnerProps = $props();
|
||||
</script>
|
||||
|
||||
<Sonner
|
||||
theme={mode.current}
|
||||
class="toaster group"
|
||||
style="--normal-bg: var(--color-popover); --normal-text: var(--color-popover-foreground); --normal-border: var(--color-border);"
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet loadingIcon()}
|
||||
<Loader2Icon class="size-4 animate-spin" />
|
||||
{/snippet}
|
||||
{#snippet successIcon()}
|
||||
<CircleCheckIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet errorIcon()}
|
||||
<OctagonXIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet infoIcon()}
|
||||
<InfoIcon class="size-4" />
|
||||
{/snippet}
|
||||
{#snippet warningIcon()}
|
||||
<TriangleAlertIcon class="size-4" />
|
||||
{/snippet}
|
||||
</Sonner>
|
||||
@@ -17,13 +17,12 @@ export const genDescription = command(v.string(), async (data) => {
|
||||
const outputBuffer = await image
|
||||
.rotate()
|
||||
.resize({ fit: 'outside', height: 300, width: 300 })
|
||||
.jpeg({ quality: 80 }) // adjust if needed
|
||||
.jpeg({ quality: 70 }) // adjust if needed
|
||||
.toBuffer();
|
||||
|
||||
const description = await LLMDescribe(
|
||||
`data:image/jpeg;base64,${outputBuffer.toString('base64')}`
|
||||
);
|
||||
console.log(description);
|
||||
|
||||
return description;
|
||||
});
|
||||
|
||||
@@ -75,9 +75,6 @@ export async function sendNewInquiryEmail(inquiryId: number) {
|
||||
};
|
||||
const replyToken = jwt.sign(tokenPayload, process.env.JWT_SECRET!);
|
||||
|
||||
console.log(item);
|
||||
console.log(item.threads);
|
||||
console.log(replyToken);
|
||||
// Send mail with defined transport object
|
||||
await transporter.sendMail({
|
||||
from: `Westuffind Notifier <${process.env.EMAIL_USER}>`,
|
||||
|
||||
@@ -21,9 +21,6 @@ export async function LLMDescribe(imageData: string) {
|
||||
temperature: 0.2
|
||||
};
|
||||
|
||||
console.log('AIing it');
|
||||
console.log(payload);
|
||||
|
||||
const res = await fetch(
|
||||
`http://${process.env.LLAMA_HOST!}:${process.env.LLAMA_PORT!}/v1/chat/completions`,
|
||||
{
|
||||
@@ -36,8 +33,8 @@ export async function LLMDescribe(imageData: string) {
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
console.log(await res.text());
|
||||
process.exit(1);
|
||||
console.error(await res.text());
|
||||
// process.exit(1);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
+159
-53
@@ -17,26 +17,32 @@
|
||||
Westuffinder
|
||||
</h1>
|
||||
<p class="mx-auto mt-6 max-w-2xl text-lg text-muted-foreground">
|
||||
Lost something at school? Found something that isn’t yours?
|
||||
Lost something at school? Found something that isn't yours?
|
||||
This is the official place to reconnect items with their owners.
|
||||
</p>
|
||||
|
||||
<div class="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||
<div class="mt-10 flex flex-col items-center justify-center gap-4 ">
|
||||
<a
|
||||
href="/items"
|
||||
class={cn(buttonVariants({ variant: 'default', size: 'xl' }), 'text-base')}
|
||||
aria-label="Browse lost items"
|
||||
>
|
||||
Browse lost items
|
||||
</a>
|
||||
<div class="w-40 flex items-center gap-2 h-2">
|
||||
<div class="flex-1 h-px bg-border"></div>
|
||||
<span class="text-muted-foreground whitespace-nowrap">Or</span>
|
||||
<div class="flex-1 h-px bg-border"></div>
|
||||
</div>
|
||||
<Button
|
||||
size="lg"
|
||||
class="px-8"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="text-muted-foreground"
|
||||
onclick={openCreateDialog}
|
||||
aria-label="Submit a found item"
|
||||
>
|
||||
Submit a Found Item
|
||||
Submit found item
|
||||
</Button>
|
||||
<a
|
||||
href="/items"
|
||||
class={cn(buttonVariants({ variant: 'outline', size: 'lg' }), 'px-8')}
|
||||
aria-label="Browse lost items"
|
||||
>
|
||||
Browse Lost Items
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,65 +56,165 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-6xl px-6 py-20" aria-labelledby="info-cards-heading">
|
||||
<h2 id="info-cards-heading" class="sr-only">Information</h2>
|
||||
<div class="grid gap-8 md:grid-cols-3">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle id="found-item-heading">Found an Item?</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent
|
||||
class="space-y-3 text-sm text-muted-foreground"
|
||||
aria-labelledby="found-item-heading"
|
||||
>
|
||||
<p>
|
||||
Turn it in digitally in less than a minute. Add a description and where you found it.
|
||||
<!-- FEATURES -->
|
||||
<section class="mx-auto max-w-6xl px-6 py-12" aria-labelledby="features-heading">
|
||||
<h2 id="features-heading" class="text-2xl font-semibold text-center mb-12">
|
||||
Everything you need to get items back fast
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-12 md:grid-cols-2 items-center">
|
||||
<!-- Left visual / emphasis block -->
|
||||
<div class="space-y-6">
|
||||
<div class="rounded-2xl border bg-muted/30 p-6">
|
||||
<h3 class="font-semibold text-lg">Built for speed</h3>
|
||||
<p class="text-sm text-muted-foreground mt-2">
|
||||
Report and browse items in seconds. The entire flow is designed to reduce friction.
|
||||
</p>
|
||||
<Button
|
||||
class="mt-2 w-full"
|
||||
onclick={openCreateDialog}
|
||||
aria-label="Submit a found item"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border bg-muted/20 p-6">
|
||||
<h3 class="font-semibold text-lg">Organized listings</h3>
|
||||
<p class="text-sm text-muted-foreground mt-2">
|
||||
Items are structured by location and category so nothing gets buried.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-2xl border bg-muted/10 p-6">
|
||||
<h3 class="font-semibold text-lg">Trusted process</h3>
|
||||
<p class="text-sm text-muted-foreground mt-2">
|
||||
Every submission is reviewed before it's visible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right visual anchor -->
|
||||
<div class="rounded-2xl border p-10 bg-gradient-to-br from-primary/10 to-transparent">
|
||||
<h3 class="text-xl font-semibold">Designed for school life</h3>
|
||||
<p class="text-sm text-muted-foreground mt-4">
|
||||
Backpacks, phones, notebooks, jackets — the things that actually get lost daily.
|
||||
Everything here is tuned for real student use, not generic “lost & found” systems.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 text-xs text-muted-foreground">
|
||||
Over <span class="font-medium text-foreground">120+</span> items successfully returned
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- HOW IT WORKS (Vertical Timeline Style) -->
|
||||
<section class="mx-auto max-w-4xl px-6 py-12" aria-labelledby="how-heading">
|
||||
<h2 id="how-heading" class="text-2xl font-semibold text-center mb-12">
|
||||
How it works
|
||||
</h2>
|
||||
|
||||
<div class="relative border-l pl-8 space-y-10">
|
||||
<div class="relative">
|
||||
<div class="absolute -left-[40px] top-1 h-4 w-4 rounded-full bg-primary"></div>
|
||||
<h3 class="font-semibold">Someone finds something</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
An item is discovered in a classroom, hallway, locker room, or common area.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="absolute -left-[40px] top-1 h-4 w-4 rounded-full bg-primary/70"></div>
|
||||
<h3 class="font-semibold">It gets submitted</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
A quick submission captures what it is and where it was found.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="absolute -left-[40px] top-1 h-4 w-4 rounded-full bg-primary/40"></div>
|
||||
<h3 class="font-semibold">It makes its way home</h3>
|
||||
<p class="text-sm text-muted-foreground mt-1">
|
||||
The owner identifies it and retrieves it through staff.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ACTION / INFO (Staggered Bento Style Instead of 3 Equal Cards) -->
|
||||
<section class="mx-auto max-w-6xl px-6 py-24" aria-labelledby="info-heading">
|
||||
<h2 id="info-heading" class="sr-only">Actions</h2>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<!-- Large primary card -->
|
||||
<Card class="">
|
||||
<CardHeader>
|
||||
<CardTitle>Found something?</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4 text-sm text-muted-foreground">
|
||||
<p>
|
||||
Help return it in under a minute. Every submission makes a difference.
|
||||
</p>
|
||||
<Button variant="outline" class="w-full" onclick={openCreateDialog}>
|
||||
Submit Item
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<!-- Side card -->
|
||||
<Card class="">
|
||||
<CardHeader>
|
||||
<CardTitle id="lost-item-heading">Lost Something?</CardTitle>
|
||||
<CardTitle>Looking for yours?</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent
|
||||
class="space-y-3 text-sm text-muted-foreground"
|
||||
aria-labelledby="lost-item-heading"
|
||||
>
|
||||
<CardContent class="space-y-4 text-sm text-muted-foreground">
|
||||
<p>
|
||||
Browse items that have been turned in by students and staff.
|
||||
Check the live list of items turned in around school.
|
||||
</p>
|
||||
<a
|
||||
href="/items"
|
||||
class={cn(buttonVariants({ variant: 'outline', size: 'lg' }), 'mt-2 w-full')}
|
||||
aria-label="Browse found items"
|
||||
class={cn(buttonVariants({ variant: 'outline' }), 'w-full')}
|
||||
>
|
||||
Browse Items
|
||||
Browse
|
||||
</a>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle id="safe-heading">Safe & School-Run</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent
|
||||
class="space-y-3 text-sm text-muted-foreground"
|
||||
aria-labelledby="safe-heading"
|
||||
>
|
||||
<p>
|
||||
Managed by Waukesha West staff. Items are reviewed before being listed.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<!-- Wide bottom emphasis card -->
|
||||
<Card class="md:col-span-3 border-muted bg-muted/20 mt-6">
|
||||
<CardContent class=" flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="font-semibold">Built on trust</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
A simple system powered by students and staff working together.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-muted-foreground">
|
||||
<span class="font-medium text-foreground">120+</span> items returned and counting
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<!-- IMPORTANT DISCLAIMER -->
|
||||
<section class="mx-auto max-w-6xl px-6 pb-20" aria-labelledby="policy-heading">
|
||||
<Card class="border-amber-500/40 bg-amber-500/5">
|
||||
<CardHeader>
|
||||
<CardTitle id="policy-heading">Important Notice</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="text-sm text-muted-foreground space-y-3">
|
||||
<p>
|
||||
This platform is intended for everyday personal items such as notebooks, clothing,
|
||||
water bottles, and similar belongings commonly misplaced at school.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Items of significant monetary, legal, or sensitive value (including but not limited to cash,
|
||||
credit cards, IDs, or high-value electronics) should <strong>not</strong> be handled through
|
||||
this system.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Such items must be immediately reported to the main office or appropriate school staff for
|
||||
proper handling and documentation.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-6xl px-6 pb-24" aria-labelledby="staff-heading">
|
||||
|
||||
@@ -1,17 +1,76 @@
|
||||
import type { PageServerLoad } from '$types';
|
||||
import sql from '$lib/db/db.server';
|
||||
import type { User } from '$lib/types/user';
|
||||
import type { User, UserSettings } from '$lib/types/user';
|
||||
import { type Actions, error, fail } from '@sveltejs/kit';
|
||||
import { getFormString, getRequiredFormString } from '$lib/shared';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { setJWTCookie } from '$lib/auth/index.server';
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
if (!locals || !locals.user) {
|
||||
throw Error('Need to be logged in!');
|
||||
}
|
||||
|
||||
const [userData]: User[] = await sql`
|
||||
const [userData] = await sql`
|
||||
SELECT * FROM users WHERE id = ${locals.user.id}
|
||||
`;
|
||||
|
||||
console.log(userData);
|
||||
|
||||
userData.settings = JSON.parse(userData.settings);
|
||||
return { userData };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request, url, locals, params, cookies }) => {
|
||||
if (!locals || !locals.user) {
|
||||
throw error(403, 'Need to be logged in!');
|
||||
}
|
||||
|
||||
const data = await request.formData();
|
||||
|
||||
let name: string;
|
||||
let email: string;
|
||||
let newPassword: string | null;
|
||||
let retypeNewPassword: string | null;
|
||||
let staleItemDays: number;
|
||||
let notifyAllApprovedInquiries: boolean;
|
||||
let notifyAllTurnedInInquiries: boolean;
|
||||
|
||||
try {
|
||||
name = getRequiredFormString(data, 'name');
|
||||
email = getRequiredFormString(data, 'email');
|
||||
newPassword = getFormString(data, 'newPassword');
|
||||
retypeNewPassword = getFormString(data, 'retypeNewPassword');
|
||||
staleItemDays = parseInt(getRequiredFormString(data, 'staleItemDays'));
|
||||
notifyAllApprovedInquiries = getFormString(data, 'notifyAllApprovedInquiries') == 'on';
|
||||
notifyAllTurnedInInquiries = getFormString(data, 'notifyAllTurnedInInquiries') == 'on';
|
||||
|
||||
if (newPassword !== retypeNewPassword) {
|
||||
fail(400, { password: 'New passwords dont match!' });
|
||||
}
|
||||
const passwordHash = newPassword ? await bcrypt.hash(newPassword, 12) : null;
|
||||
|
||||
const settings: UserSettings = {
|
||||
staleItemDays,
|
||||
notifyAllApprovedInquiries,
|
||||
notifyAllTurnedInInquiries
|
||||
};
|
||||
|
||||
const [res] = await sql`
|
||||
UPDATE users SET name = ${name},
|
||||
email = ${email},
|
||||
${passwordHash ? sql`password_hash = ${passwordHash},` : sql``}
|
||||
settings = ${JSON.stringify(settings)}
|
||||
WHERE id = ${locals.user.id}
|
||||
RETURNING *;
|
||||
`;
|
||||
res.settings = JSON.parse(res.settings);
|
||||
setJWTCookie(cookies, res as User);
|
||||
} catch (e) {
|
||||
return fail(400, {
|
||||
message: e instanceof Error ? e.message : 'Unknown error occurred',
|
||||
success: false
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
} satisfies Actions;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- src/routes/account/+page.svelte -->
|
||||
<!-- src/routes/account/++page.svelte -->
|
||||
<script lang="ts">
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
@@ -9,36 +9,44 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { DefaultUserSettings } from '$lib/types/user';
|
||||
import { enhance } from '$app/forms';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { Toaster } from '$lib/components/ui/sonner';
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
|
||||
let { data } = $props();
|
||||
let { data, form } = $props();
|
||||
|
||||
function signOut() {
|
||||
document.cookie = 'jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
let form = $derived({
|
||||
name: data.userData.name,
|
||||
email: data.userData.email,
|
||||
staleItemDays:
|
||||
data.userData.settings?.staleItemDays ?? DefaultUserSettings.staleItemDays,
|
||||
notifyAllApprovedInquiries:
|
||||
data.userData.settings?.notifyAllApprovedInquiries ?? false,
|
||||
notifyAllTurnedInInquiries:
|
||||
data.userData.settings?.notifyAllTurnedInInquiries ?? false
|
||||
});
|
||||
// Use top-level variables for two-way binding. Binding to object properties
|
||||
// like `form.name` doesn't create proper reactive two-way bindings in Svelte.
|
||||
let name: string = $state(data.userData.name);
|
||||
let email: string = $state(data.userData.email);
|
||||
let staleItemDays: number =
|
||||
$state(data.userData.settings?.staleItemDays ?? DefaultUserSettings.staleItemDays);
|
||||
let notifyAllApprovedInquiries: boolean =
|
||||
$state(data.userData.settings?.notifyAllApprovedInquiries ?? false);
|
||||
let notifyAllTurnedInInquiries: boolean =
|
||||
$state(data.userData.settings?.notifyAllTurnedInInquiries ?? false);
|
||||
|
||||
const formatDate = (date: Date | string) => {
|
||||
const d = new Date(date);
|
||||
return d.toLocaleString();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Account</title>
|
||||
</svelte:head>
|
||||
|
||||
<Toaster></Toaster>
|
||||
|
||||
<div class="container mx-auto max-w-3xl py-10">
|
||||
|
||||
<Card class="rounded-2xl shadow-sm">
|
||||
<CardHeader>
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -49,7 +57,18 @@
|
||||
|
||||
<CardContent class="space-y-6">
|
||||
<!-- Editable Profile Form -->
|
||||
<form method="POST" use:enhance class="space-y-6">
|
||||
<form method="POST" class="space-y-6" use:enhance={({ formElement, formData, action, cancel, submitter }) => {
|
||||
return async ({ result, update }) => {
|
||||
if (result.status === 200) {
|
||||
toast('Saved', {
|
||||
icon: CheckIcon,
|
||||
description: 'Your account has been saved!',
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<Label for="username">Username</Label>
|
||||
@@ -57,64 +76,95 @@
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="name">Full Name</Label>
|
||||
<Input id="name" name="name" bind:value={form.name} />
|
||||
<Label for="name">Full Name<span class="text-error">*</span></Label>
|
||||
<Input id="name" name="name" bind:value={name} />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 sm:col-span-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Input id="email" name="email" type="email" bind:value={form.email} />
|
||||
<Label for="email">Email<span class="text-error">*</span></Label>
|
||||
<Input id="email" name="email" type="email" bind:value={email} />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="staleItemDays">Stale Item Days</Label>
|
||||
<Input
|
||||
id="staleItemDays"
|
||||
name="staleItemDays"
|
||||
type="number"
|
||||
bind:value={form.staleItemDays}
|
||||
min="0"
|
||||
/>
|
||||
<Label for="newPassword">New Password</Label>
|
||||
<Input id="newPassword" name="newPassword" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="retypeNewPassword">Retype New Password</Label>
|
||||
<Input id="retypeNewPassword" name="retypeNewPassword" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-lg font-semibold">Notifications</h2>
|
||||
|
||||
<h2 class="text-lg font-semibold">Settings</h2>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="font-medium">Notify All Approved</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Receive notifications when inquiries are approved.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
name="notifyAllApprovedInquiries"
|
||||
bind:checked={form.notifyAllApprovedInquiries}
|
||||
<Label for="staleItemDays" class="text-base">
|
||||
<div>
|
||||
<p class="font-medium">Stale Item Days</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Number of days without activity before items show up as stale.
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
<Input
|
||||
class="w-min inline-block"
|
||||
id="staleItemDays"
|
||||
name="staleItemDays"
|
||||
type="number"
|
||||
bind:value={staleItemDays}
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="font-medium">Notify All Turned In</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Receive notifications when inquiries are turned in.
|
||||
</p>
|
||||
</div>
|
||||
<Label for="notifyAllApprovedInquiries" class="text-base">
|
||||
<div>
|
||||
|
||||
<p class="font-medium">Notify for All Approved Items</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Receive notifications for all approved items, not just yours.
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
<Switch
|
||||
name="notifyAllApprovedInquiries"
|
||||
id="notifyAllApprovedInquiries"
|
||||
bind:checked={notifyAllApprovedInquiries}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<Label for="notifyAllTurnedInInquiries" class="text-base">
|
||||
<div>
|
||||
|
||||
<p class="font-medium">Notify for All Turned In Items</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Receive notifications for all items turned in to the school lost-and-found.
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
<Switch
|
||||
name="notifyAllTurnedInInquiries"
|
||||
bind:checked={form.notifyAllTurnedInInquiries}
|
||||
id="notifyAllTurnedInInquiries"
|
||||
bind:checked={notifyAllTurnedInInquiries}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between pt-4">
|
||||
<div class="text-sm text-muted-foreground">
|
||||
Member since {formatDate(data.userData.createdAt)} · Last sign in {formatDate(
|
||||
data.userData.lastSignIn
|
||||
)}
|
||||
<p>
|
||||
|
||||
Member since {formatDate(data.userData.createdAt)}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
Last sign in {formatDate(data.userData.lastSignIn)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
|
||||
import * as Field from '$lib/components/ui/field/index.js';
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import * as Select from '$lib/components/ui/select/index.js';
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js';
|
||||
import ImageUpload from '$lib/components/custom/image-upload/image-upload.svelte';
|
||||
|
||||
let month = $state<string>();
|
||||
let year = $state<string>();
|
||||
</script>
|
||||
|
||||
<div class="w-full max-w-lg mt-8">
|
||||
<form>
|
||||
<Field.Group>
|
||||
<Field.Set>
|
||||
<Field.Legend>Submit Found Item</Field.Legend>
|
||||
<Field.Description
|
||||
>Your item will need to be approved before becoming public
|
||||
</Field.Description>
|
||||
<Field.Group>
|
||||
<Field.Field>
|
||||
<Field.Label for="image-upload">
|
||||
Image
|
||||
</Field.Label>
|
||||
<ImageUpload></ImageUpload>
|
||||
</Field.Field>
|
||||
<Field.Field>
|
||||
<Field.Label for="checkout-7j9-card-name-43j"
|
||||
>Name on Card
|
||||
</Field.Label
|
||||
>
|
||||
<Input
|
||||
id="checkout-7j9-card-name-43j"
|
||||
placeholder="John Doe"
|
||||
required
|
||||
/>
|
||||
</Field.Field>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<Field.Field class="col-span-2">
|
||||
<Field.Label for="checkout-7j9-card-number-uw1">
|
||||
Card Number
|
||||
</Field.Label>
|
||||
<Input
|
||||
id="checkout-7j9-card-number-uw1"
|
||||
placeholder="1234 5678 9012 3456"
|
||||
required
|
||||
/>
|
||||
<Field.Description>Enter your 16-digit number.</Field.Description>
|
||||
</Field.Field>
|
||||
<Field.Field class="col-span-1">
|
||||
<Field.Label for="checkout-7j9-cvv">CVV</Field.Label>
|
||||
<Input id="checkout-7j9-cvv" placeholder="123" required />
|
||||
</Field.Field>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<Field.Field>
|
||||
<Field.Label for="checkout-7j9-exp-month-ts6">Month</Field.Label>
|
||||
<Select.Root type="single" bind:value={month}>
|
||||
<Select.Trigger id="checkout-7j9-exp-month-ts6">
|
||||
<span>
|
||||
{month || "MM"}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="01">01</Select.Item>
|
||||
<Select.Item value="02">02</Select.Item>
|
||||
<Select.Item value="03">03</Select.Item>
|
||||
<Select.Item value="04">04</Select.Item>
|
||||
<Select.Item value="05">05</Select.Item>
|
||||
<Select.Item value="06">06</Select.Item>
|
||||
<Select.Item value="07">07</Select.Item>
|
||||
<Select.Item value="08">08</Select.Item>
|
||||
<Select.Item value="09">09</Select.Item>
|
||||
<Select.Item value="10">10</Select.Item>
|
||||
<Select.Item value="11">11</Select.Item>
|
||||
<Select.Item value="12">12</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Field.Field>
|
||||
<Field.Field>
|
||||
<Field.Label for="checkout-7j9-exp-year-f59">Year</Field.Label>
|
||||
<Select.Root type="single" bind:value={year}>
|
||||
<Select.Trigger id="checkout-7j9-exp-year-f59">
|
||||
<span>
|
||||
{year || "YYYY"}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="2024">2024</Select.Item>
|
||||
<Select.Item value="2025">2025</Select.Item>
|
||||
<Select.Item value="2026">2026</Select.Item>
|
||||
<Select.Item value="2027">2027</Select.Item>
|
||||
<Select.Item value="2028">2028</Select.Item>
|
||||
<Select.Item value="2029">2029</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Field.Field>
|
||||
</div>
|
||||
</Field.Group>
|
||||
</Field.Set>
|
||||
<Field.Separator />
|
||||
<Field.Set>
|
||||
<Field.Legend>Billing Address</Field.Legend>
|
||||
<Field.Description>
|
||||
The billing address associated with your payment method
|
||||
</Field.Description>
|
||||
<Field.Group>
|
||||
<Field.Field orientation="horizontal">
|
||||
<Checkbox id="checkout-7j9-same-as-shipping-wgm" checked={true} />
|
||||
<Field.Label
|
||||
for="checkout-7j9-same-as-shipping-wgm"
|
||||
class="font-normal"
|
||||
>
|
||||
Same as shipping address
|
||||
</Field.Label>
|
||||
</Field.Field>
|
||||
</Field.Group>
|
||||
</Field.Set>
|
||||
<Field.Separator />
|
||||
<Field.Set>
|
||||
<Field.Group>
|
||||
<Field.Field>
|
||||
<Field.Label for="checkout-7j9-optional-comments"
|
||||
>Comments
|
||||
</Field.Label
|
||||
>
|
||||
<Textarea
|
||||
id="checkout-7j9-optional-comments"
|
||||
placeholder="Add any additional comments"
|
||||
class="resize-none"
|
||||
/>
|
||||
</Field.Field>
|
||||
</Field.Group>
|
||||
</Field.Set>
|
||||
<Field.Field orientation="horizontal">
|
||||
<Button type="submit">Submit</Button>
|
||||
<Button variant="outline" type="button">Cancel</Button>
|
||||
</Field.Field>
|
||||
</Field.Group>
|
||||
</form>
|
||||
</div>
|
||||
@@ -35,7 +35,8 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
WHERE im.thread_id = t.id
|
||||
) m ON TRUE
|
||||
${searchQuery ? sql`WHERE word_similarity(${searchQuery}, i.description) > 0.3` : sql``}
|
||||
GROUP BY i.id;
|
||||
GROUP BY i.id
|
||||
ORDER BY i.found_date DESC;
|
||||
`;
|
||||
items.forEach((item) =>
|
||||
item.threads?.forEach((thread) => (thread.createdAt = new Date(thread.createdAt)))
|
||||
@@ -110,7 +111,7 @@ export const load: PageServerLoad = async ({ url, locals }) => {
|
||||
// Not logged in or fallback: simple item list (no threads)
|
||||
let items: Item[];
|
||||
if (!searchQuery) {
|
||||
items = await sql`SELECT * FROM items;`;
|
||||
items = await sql`SELECT * FROM items ORDER BY found_date DESC;`;
|
||||
} else {
|
||||
items = await sql`
|
||||
SELECT * FROM items
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
{#each data.items as item (item.id)}
|
||||
{#if item.approvedDate === null}
|
||||
<ItemListing item={item} admin={data.user !== null} editCallback={openEditDialog}
|
||||
<ItemListing item={item} user={data.user} editCallback={openEditDialog}
|
||||
inquireCallback={openInquireDialog} claimCallback={openClaimDialog} />
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
{#each data.items as item (item.id)}
|
||||
{#if item.approvedDate !== null && item.claimedDate === null}
|
||||
<ItemListing item={item} admin={data.user !== null} editCallback={openEditDialog}
|
||||
<ItemListing item={item} user={data.user} editCallback={openEditDialog}
|
||||
inquireCallback={openInquireDialog} claimCallback={openClaimDialog} />
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -115,7 +115,7 @@
|
||||
</FieldSeparator>
|
||||
{#each data.items as item (item.id)}
|
||||
{#if item.claimedDate !== null}
|
||||
<ItemListing item={item} admin={data.user !== null} editCallback={openEditDialog}
|
||||
<ItemListing item={item} user={data.user} editCallback={openEditDialog}
|
||||
inquireCallback={openInquireDialog} claimCallback={openClaimDialog} />
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -125,7 +125,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Button class="fixed shadow-lg bottom-6 right-6 rounded-xl md:hidden p-6 text-xl" size="default"
|
||||
<Button class="fixed shadow-lg bottom-6 right-6 rounded-xl md:hidden p-6 text-xl z-20" size="default"
|
||||
onclick={openCreateDialog}>
|
||||
<PlusIcon size={32} />
|
||||
New item
|
||||
|
||||
@@ -9,7 +9,6 @@ import type { Item } from '$lib/types/item';
|
||||
|
||||
export const load: PageServerLoad = async ({ url, locals, params }) => {
|
||||
const token: string | undefined = url.searchParams.get('token');
|
||||
// const itemId: string = params.itemId;
|
||||
const inquiryId: string = params.inquiryId;
|
||||
|
||||
if (token) {
|
||||
@@ -48,15 +47,12 @@ export const load: PageServerLoad = async ({ url, locals, params }) => {
|
||||
WHERE i.id = (SELECT item_id FROM inquiry_threads WHERE id = ${inquiryId})
|
||||
GROUP BY i.id;`;
|
||||
|
||||
console.log(item);
|
||||
|
||||
return { item };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
reply: async ({ request, url, locals, params }) => {
|
||||
const token: string | undefined = url.searchParams.get('token');
|
||||
const itemId: number = params.itemId;
|
||||
const inquiryId: number = params.inquiryId;
|
||||
|
||||
let sender: Sender | undefined;
|
||||
@@ -86,21 +82,19 @@ export const actions: Actions = {
|
||||
|
||||
const body = getRequiredFormString(data, 'message');
|
||||
|
||||
console.log(inquiryId, sender, body);
|
||||
const response = await sql`
|
||||
INSERT INTO inquiry_messages (thread_id, sender, body)
|
||||
VALUES (${inquiryId}, ${sender}, ${body})
|
||||
RETURNING id;
|
||||
`;
|
||||
|
||||
// TODO: Double check logic
|
||||
let nextReplySender: Sender;
|
||||
if (sender === Sender.ADMIN || sender === Sender.FINDER) {
|
||||
nextReplySender = Sender.INQUIRER;
|
||||
} else {
|
||||
nextReplySender = Sender.FINDER;
|
||||
}
|
||||
await sendInquiryMessageEmail(response[0].threadId, nextReplySender);
|
||||
await sendInquiryMessageEmail(inquiryId, nextReplySender);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import * as item from 'valibot';
|
||||
import NoImagePlaceholder from '$lib/components/custom/no-image-placeholder.svelte';
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
import { dateFormatOptions } from '$lib/shared';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -35,6 +36,14 @@
|
||||
}
|
||||
};
|
||||
|
||||
const dateTimeFormatOptions: Intl.DateTimeFormatOptions = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<div class="max-w-2xl mx-auto p-4">
|
||||
@@ -55,22 +64,17 @@
|
||||
<div class="">
|
||||
<div class="flex-1">{data.item.description}</div>
|
||||
{#if data.item.foundLocation}
|
||||
<div class="mt-2">
|
||||
<LocationIcon class="float-left mr-1" size={24} />
|
||||
<div>{data.item.foundLocation}</div>
|
||||
</div>
|
||||
<span class="mt-2"><strong class="mr-1">Found at:</strong> {item.foundLocation}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Separator class="my-6" />
|
||||
|
||||
<div class="relative">
|
||||
<!-- Conversation -->
|
||||
<div class="flex flex-col gap-6 relative">
|
||||
{#if data.item.threads}
|
||||
{#each data.item.threads[0].messages as msg (msg.id)}
|
||||
<div class="flex items-start gap-4">
|
||||
<!-- Avatar -->
|
||||
<div class="flex flex-col items-center">
|
||||
<div
|
||||
class={`flex items-center justify-center w-10 h-10 rounded-full text-white ${senderColor(msg.sender)}`}>
|
||||
@@ -78,10 +82,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message body -->
|
||||
<div class="bg-card rounded-lg p-3 shadow-sm max-w-xl">
|
||||
<p class="text-sm">{msg.body}</p>
|
||||
<span class="text-xs text-muted-foreground mt-1 block">{msg.createdAt.toLocaleString()}</span>
|
||||
<span class="text-xs text-muted-foreground mt-1 block">{new Date(msg.createdAt as unknown as string).toLocaleDateString('en-US', dateTimeFormatOptions)}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
@@ -91,11 +94,11 @@
|
||||
|
||||
<!-- Input area -->
|
||||
<form class="mt-6 flex flex-col gap-2" method="post" action="?/reply&token={page.url.searchParams.get('token')}">
|
||||
<Textarea
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Write a message..."
|
||||
class="resize-none"
|
||||
name="message"
|
||||
rows={3}
|
||||
/>
|
||||
<Button class="self-end" type="submit">Send
|
||||
<SendIcon />
|
||||
|
||||
@@ -34,20 +34,11 @@
|
||||
<Field>
|
||||
<div class="flex items-center">
|
||||
<FieldLabel for="password">Password</FieldLabel>
|
||||
<!-- <a href="##" class="ms-auto text-sm underline-offset-4 hover:underline">-->
|
||||
<!-- Forgot your password?-->
|
||||
<!-- </a>-->
|
||||
</div>
|
||||
<Input id="password" type="password" required />
|
||||
</Field>
|
||||
<Field>
|
||||
<Button type="submit">Login</Button>
|
||||
</Field>
|
||||
<!-- <Field>-->
|
||||
<!-- <FieldDescription class="text-center">-->
|
||||
<!-- Don't have an account?-->
|
||||
<!-- <a href="/signup" class="underline underline-offset-4">Sign up</a>-->
|
||||
<!-- </FieldDescription>-->
|
||||
<!-- </Field>-->
|
||||
</FieldGroup>
|
||||
</form>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import { render } from 'vitest-browser-svelte';
|
||||
import Page from './items/+page.svelte';
|
||||
|
||||
describe('/+page.svelte', () => {
|
||||
describe('/++page.svelte', () => {
|
||||
it('should render h1', async () => {
|
||||
render(Page);
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user