feat: cards
This commit is contained in:
parent
4adedd96ee
commit
71c69b7b80
596
client/package-lock.json
generated
596
client/package-lock.json
generated
@ -734,9 +734,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz",
|
||||
"integrity": "sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==",
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
|
||||
"integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -855,6 +855,23 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
|
||||
@ -868,6 +885,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
|
||||
@ -1666,28 +1690,28 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@scalar/api-client": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/api-client/-/api-client-2.3.1.tgz",
|
||||
"integrity": "sha512-fRfWWFCTnXpCVVOQLeqf+NJmeWqkUIOn4u4wwRGYcmDNYZcWgfJtROHjISBEnwjCs18eMZsS0xB9j2fIiw4bAw==",
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/api-client/-/api-client-2.3.5.tgz",
|
||||
"integrity": "sha512-bVBP8H3laa4VG3dExv4Ak/Kf+q9z8aK/Glpv6CPE0wxgZJWxwkJoSrvwxUE+46JktSObUrhu4xQiqSCmBz8esQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@headlessui/vue": "^1.7.20",
|
||||
"@scalar/components": "0.13.35",
|
||||
"@scalar/components": "0.13.37",
|
||||
"@scalar/draggable": "0.1.11",
|
||||
"@scalar/icons": "0.1.3",
|
||||
"@scalar/import": "0.3.0",
|
||||
"@scalar/oas-utils": "0.2.118",
|
||||
"@scalar/import": "0.3.2",
|
||||
"@scalar/oas-utils": "0.2.120",
|
||||
"@scalar/object-utils": "1.1.13",
|
||||
"@scalar/openapi-parser": "0.10.10",
|
||||
"@scalar/openapi-types": "0.1.9",
|
||||
"@scalar/postman-to-openapi": "0.1.41",
|
||||
"@scalar/postman-to-openapi": "0.1.43",
|
||||
"@scalar/snippetz": "0.2.16",
|
||||
"@scalar/themes": "0.9.77",
|
||||
"@scalar/types": "0.1.0",
|
||||
"@scalar/use-codemirror": "0.11.80",
|
||||
"@scalar/use-hooks": "0.1.31",
|
||||
"@scalar/themes": "0.9.79",
|
||||
"@scalar/types": "0.1.1",
|
||||
"@scalar/use-codemirror": "0.11.82",
|
||||
"@scalar/use-hooks": "0.1.33",
|
||||
"@scalar/use-toasts": "0.7.9",
|
||||
"@scalar/use-tooltip": "1.0.6",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
@ -1709,44 +1733,25 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/api-client/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/api-reference": {
|
||||
"version": "1.28.1",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/api-reference/-/api-reference-1.28.1.tgz",
|
||||
"integrity": "sha512-axhOMIDcDkD7QlFoicfDfILOkEKSPS/P6oiTqnErkTvKifFFhMxzUY/gtjkkbpMmnEi9bnlbM8kPFeRgXiSISA==",
|
||||
"version": "1.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/api-reference/-/api-reference-1.28.5.tgz",
|
||||
"integrity": "sha512-GNBQae0OCk39KnOmJNtciCc0gKY0BtWYZt1wkqogetm8d2D4JypzxpR4+TqKJeBoASsKF45XogXuKlDbywOGZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/vue": "^1.0.2",
|
||||
"@headlessui/vue": "^1.7.20",
|
||||
"@scalar/api-client": "2.3.1",
|
||||
"@scalar/code-highlight": "0.0.24",
|
||||
"@scalar/components": "0.13.35",
|
||||
"@scalar/oas-utils": "0.2.118",
|
||||
"@scalar/api-client": "2.3.5",
|
||||
"@scalar/code-highlight": "0.0.25",
|
||||
"@scalar/components": "0.13.37",
|
||||
"@scalar/oas-utils": "0.2.120",
|
||||
"@scalar/openapi-parser": "0.10.10",
|
||||
"@scalar/openapi-types": "0.1.9",
|
||||
"@scalar/snippetz": "0.2.16",
|
||||
"@scalar/themes": "0.9.77",
|
||||
"@scalar/types": "0.1.0",
|
||||
"@scalar/use-hooks": "0.1.31",
|
||||
"@scalar/themes": "0.9.79",
|
||||
"@scalar/types": "0.1.1",
|
||||
"@scalar/use-hooks": "0.1.33",
|
||||
"@scalar/use-toasts": "0.7.9",
|
||||
"@unhead/vue": "^1.11.11",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
@ -1760,29 +1765,10 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/api-reference/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/code-highlight": {
|
||||
"version": "0.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/code-highlight/-/code-highlight-0.0.24.tgz",
|
||||
"integrity": "sha512-xQFt5yVbNr3+7dmMsQrV/yc0Zi9rMV9E9rSvzroD0TQvwi39rAiMmdQzwLXT/S9B40foIJiP/3y6FmxDEf3DoA==",
|
||||
"version": "0.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/code-highlight/-/code-highlight-0.0.25.tgz",
|
||||
"integrity": "sha512-rmiXaAoL3Zl+OycIO1CMj8apaeAU/p41EmCpHTxInZiFVW0++iClce2fun1lK6qjTMZneR6UwE4qBKiUUVLCpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1809,18 +1795,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/components": {
|
||||
"version": "0.13.35",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/components/-/components-0.13.35.tgz",
|
||||
"integrity": "sha512-R6qTijAyk/PAmWi4knomn13VQr85Jyevr+L5HkRMsPe/VC+YItcvD2JJhMK1vAZ/v9dnfByBbCq5ZcPIXa3n5A==",
|
||||
"version": "0.13.37",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/components/-/components-0.13.37.tgz",
|
||||
"integrity": "sha512-bhJxg0I63nUH0qoZgb8nyHKCSzL8L9widP2WIYymIvXpCFLwCvF64Z0CAbihwgXxq0YblPvNM+g5N3dRtmXqdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.2",
|
||||
"@floating-ui/vue": "^1.0.2",
|
||||
"@headlessui/vue": "^1.7.20",
|
||||
"@scalar/code-highlight": "0.0.24",
|
||||
"@scalar/themes": "0.9.77",
|
||||
"@scalar/use-hooks": "0.1.31",
|
||||
"@scalar/code-highlight": "0.0.25",
|
||||
"@scalar/themes": "0.9.79",
|
||||
"@scalar/use-hooks": "0.1.33",
|
||||
"@scalar/use-toasts": "0.7.9",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
"cva": "1.0.0-beta.2",
|
||||
@ -1834,25 +1820,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/components/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/components/node_modules/tailwind-merge": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
|
||||
@ -1891,13 +1858,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/import": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/import/-/import-0.3.0.tgz",
|
||||
"integrity": "sha512-5MYEaomfIPwURC6j5X2ZY9ei9JEvRfU9xE99uWK4ecisLACWC6UwxNvv+eJmx0R3bJvkN6driYmhjSSCARx55w==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/import/-/import-0.3.2.tgz",
|
||||
"integrity": "sha512-de7IDZgEYOhhgaq8lFFbfiJ4Hx/ITIXvuLA8SfSl5UQKg+LxiOnnn/5PSKF+pnjWmUf7Kz/ds0eHOXpZv5iMkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scalar/oas-utils": "0.2.118",
|
||||
"@scalar/oas-utils": "0.2.120",
|
||||
"@scalar/openapi-parser": "0.10.10",
|
||||
"yaml": "^2.4.5"
|
||||
},
|
||||
@ -1906,17 +1873,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/oas-utils": {
|
||||
"version": "0.2.118",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/oas-utils/-/oas-utils-0.2.118.tgz",
|
||||
"integrity": "sha512-8e8cgsJQT3p4/2iSj3os/eYckjeeLpW2CgMopxiNiT//utnLRgc9uZPcQJY8f4c791Ro3/9jYtYNWfQ+ENoNXA==",
|
||||
"version": "0.2.120",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/oas-utils/-/oas-utils-0.2.120.tgz",
|
||||
"integrity": "sha512-npu0uLClqqXVZfxMdKBWxkWCmONK0jKaUcfmVhGza9Jij5aJyvdfDw6vH/Hh+DghgECwAvQLQeIBZTxjr9ufzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@hyperjump/json-schema": "^1.9.6",
|
||||
"@scalar/object-utils": "1.1.13",
|
||||
"@scalar/openapi-types": "0.1.9",
|
||||
"@scalar/themes": "0.9.77",
|
||||
"@scalar/types": "0.1.0",
|
||||
"@scalar/themes": "0.9.79",
|
||||
"@scalar/types": "0.1.1",
|
||||
"flatted": "^3.3.1",
|
||||
"microdiff": "^1.4.0",
|
||||
"nanoid": "^5.0.9",
|
||||
@ -1927,25 +1894,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/oas-utils/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/object-utils": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/object-utils/-/object-utils-1.1.13.tgz",
|
||||
@ -1979,45 +1927,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/openapi-parser/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/openapi-parser/node_modules/ajv-draft-04": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
||||
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/openapi-parser/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@scalar/openapi-types": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/openapi-types/-/openapi-types-0.1.9.tgz",
|
||||
@ -2029,13 +1938,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/postman-to-openapi": {
|
||||
"version": "0.1.41",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/postman-to-openapi/-/postman-to-openapi-0.1.41.tgz",
|
||||
"integrity": "sha512-RkDTWARU628BronjZFnW4dAy4OUNXzY1yjD52pYfFRLPHNwk3tgd2l1QDujp7L/IREqmG06LGigePmxz5v7aVw==",
|
||||
"version": "0.1.43",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/postman-to-openapi/-/postman-to-openapi-0.1.43.tgz",
|
||||
"integrity": "sha512-gLOkYYPCTKYFBOwyBOKZDc0seZjntmwPTchJUr3oxGQmLB1Y5VBJ+8fXJCTp5TwKiiztjALJs79y9s7jXBdWMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scalar/oas-utils": "0.2.118",
|
||||
"@scalar/oas-utils": "0.2.120",
|
||||
"@scalar/openapi-types": "0.1.9"
|
||||
},
|
||||
"engines": {
|
||||
@ -2056,22 +1965,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/themes": {
|
||||
"version": "0.9.77",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/themes/-/themes-0.9.77.tgz",
|
||||
"integrity": "sha512-vpvJu9pF+wl/XRD+xulgasmvoItj9ICSrgq2VyYvPoUUJ1CT0Ey4JrI1g1Zl+OSiDAB/h8K9VcINDUmP+y0+uA==",
|
||||
"version": "0.9.79",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/themes/-/themes-0.9.79.tgz",
|
||||
"integrity": "sha512-zWiHCZAIjPGa8X9o/NORBPRMTMblLEz2+2RcfW9yIKNO/8H4Gz0rltiGGlJ6vX0o+qHwx7AdgfY+7njmWQR4ng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scalar/types": "0.1.0"
|
||||
"@scalar/types": "0.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/types": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.1.0.tgz",
|
||||
"integrity": "sha512-6yYwc7+PSY3bAWkQVCYxp7qPOgqaWDHmp2ceZhXaXGYbwGK7jLqwG+YAMZfl9qqo0NzoBoClUVkb8aTER1LX2A==",
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.1.1.tgz",
|
||||
"integrity": "sha512-LlUX6AmOOGoRqOMoO835V2FezM1KiO5UlvQC3poT/s7oqD6ranqwRNFxyrPz/IxClPYR+SV1yBUSNKely4ZQhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2084,9 +1993,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/use-codemirror": {
|
||||
"version": "0.11.80",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/use-codemirror/-/use-codemirror-0.11.80.tgz",
|
||||
"integrity": "sha512-MMWmSMndIjOlWSLfX048JVyDl1GbSv1Qt+R3dwq92v87JljTab0b5bzNSxhnRdlFU1HRSdPoGwILNUHYpHGndQ==",
|
||||
"version": "0.11.82",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/use-codemirror/-/use-codemirror-0.11.82.tgz",
|
||||
"integrity": "sha512-zFECln7aWKRf6iJO9oovByD59EsrOMenNLfLhneH6L+K1CrBoHFVr4czSDlom1wlr3HPg3xwpZrukoAteHYILQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2105,7 +2014,7 @@
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@replit/codemirror-css-color-picker": "^6.3.0",
|
||||
"@scalar/components": "0.13.35",
|
||||
"@scalar/components": "0.13.37",
|
||||
"codemirror": "^6.0.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"vue": "^3.5.12"
|
||||
@ -2115,13 +2024,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/use-hooks": {
|
||||
"version": "0.1.31",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/use-hooks/-/use-hooks-0.1.31.tgz",
|
||||
"integrity": "sha512-R9ofnTHKSo0C/RGIUrpZnNOe3x3B4W1PGcBgc5TnWBp9HvqLo+/X0Zpu7B/k3dpxGEAoj2dyphC99pyR7AV7pQ==",
|
||||
"version": "0.1.33",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/use-hooks/-/use-hooks-0.1.33.tgz",
|
||||
"integrity": "sha512-ENm0bWwRdAWWF/S6TbE+fFx0vP2mgEpG5APqQBomm0a41/6L2HJ/TN+9ajAvrJXGi0ULWuxihNS4Jue6tpEssA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scalar/themes": "0.9.77",
|
||||
"@scalar/themes": "0.9.79",
|
||||
"@scalar/use-toasts": "0.7.9",
|
||||
"@vueuse/core": "^10.10.0",
|
||||
"vue": "^3.5.12",
|
||||
@ -2146,25 +2055,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/use-toasts/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@scalar/use-tooltip": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@scalar/use-tooltip/-/use-tooltip-1.0.6.tgz",
|
||||
@ -2200,9 +2090,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.19.0.tgz",
|
||||
"integrity": "sha512-UTx28Ad4sYsLU//gqkEo5aFOPFBRT2uXCmXTsURqhurDCvzkVwXruJgBcHDaMiK6RKKpYRteDUaXYqZyGPgCXQ==",
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.19.2.tgz",
|
||||
"integrity": "sha512-OkW7MMGkjXtdfqdHWlyPozh/Ct1X3pthXAKTSqHm+mwmvmTBASmPE6FhwlvUgsqlCceRYL+5QUGiIJfOy0xIjQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2281,44 +2171,44 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/node": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.13.tgz",
|
||||
"integrity": "sha512-P9TmtE9Vew0vv5FwyD4bsg/dHHsIsAuUXkenuGUc5gm8fYgaxpdoxIKngCyEMEQxyCKR8PQY5V5VrrKNOx7exg==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.14.tgz",
|
||||
"integrity": "sha512-Ux9NbFkKWYE4rfUFz6M5JFLs/GEYP6ysxT8uSyPn6aTbh2K3xDE1zz++eVK4Vwx799fzMF8CID9sdHn4j/Ab8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"enhanced-resolve": "^5.18.1",
|
||||
"jiti": "^2.4.2",
|
||||
"tailwindcss": "4.0.13"
|
||||
"tailwindcss": "4.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.13.tgz",
|
||||
"integrity": "sha512-pTH3Ex5zAWC9LbS+WsYAFmkXQW3NRjmvxkKJY3NP1x0KHBWjz0Q2uGtdGMJzsa0EwoZ7wq9RTbMH1UNPceCpWw==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.14.tgz",
|
||||
"integrity": "sha512-M8VCNyO/NBi5vJ2cRcI9u8w7Si+i76a7o1vveoGtbbjpEYJZYiyc7f2VGps/DqawO56l3tImIbq2OT/533jcrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.0.13",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.0.13",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.0.13",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.0.13",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.13",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.0.13",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.0.13",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.0.13",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.0.13",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.0.13",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.0.13"
|
||||
"@tailwindcss/oxide-android-arm64": "4.0.14",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.0.14",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.0.14",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.0.14",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.14",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.0.14",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.0.14",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.0.14",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.0.14",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.0.14",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.13.tgz",
|
||||
"integrity": "sha512-+9zmwaPQ8A9ycDcdb+hRkMn6NzsmZ4YJBsW5Xqq5EdOu9xlIgmuMuJauVzDPB5BSbIWfhPdZ+le8NeRZpl1coA==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.14.tgz",
|
||||
"integrity": "sha512-VBFKC2rFyfJ5J8lRwjy6ub3rgpY186kAcYgiUr8ArR8BAZzMruyeKJ6mlsD22Zp5ZLcPW/FXMasJiJBx0WsdQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2333,9 +2223,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.13.tgz",
|
||||
"integrity": "sha512-Bj1QGlEJSjs/205CIRfb5/jeveOqzJ4pFMdRxu0gyiYWxBRyxsExXqaD+7162wnLP/EDKh6S1MC9E/1GwEhLtA==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.14.tgz",
|
||||
"integrity": "sha512-U3XOwLrefGr2YQZ9DXasDSNWGPZBCh8F62+AExBEDMLDfvLLgI/HDzY8Oq8p/JtqkAY38sWPOaNnRwEGKU5Zmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2350,9 +2240,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.13.tgz",
|
||||
"integrity": "sha512-lRTkxjTpMGXhLLM5GjZ0MtjPczMuhAo9j7PeSsaU6Imkm7W7RbrXfT8aP934kS7cBBV+HKN5U19Z0WWaORfb8Q==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.14.tgz",
|
||||
"integrity": "sha512-V5AjFuc3ndWGnOi1d379UsODb0TzAS2DYIP/lwEbfvafUaD2aNZIcbwJtYu2DQqO2+s/XBvDVA+w4yUyaewRwg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2367,9 +2257,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.13.tgz",
|
||||
"integrity": "sha512-p/YLyKhs+xFibVeAPlpMGDVMKgjChgzs12VnDFaaqRSJoOz+uJgRSKiir2tn50e7Nm4YYw35q/DRBwpDBNo1MQ==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.14.tgz",
|
||||
"integrity": "sha512-tXvtxbaZfcPfqBwW3f53lTcyH6EDT+1eT7yabwcfcxTs+8yTPqxsDUhrqe9MrnEzpNkd+R/QAjJapfd4tjWdLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2384,9 +2274,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.13.tgz",
|
||||
"integrity": "sha512-Ua/5ydE/QOTX8jHuc7M9ICWnaLi6K2MV/r+Ws2OppsOjy8tdlPbqYainJJ6Kl7ofm524K+4Fk9CQITPzeIESPw==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.14.tgz",
|
||||
"integrity": "sha512-cSeLNWWqIWeSTmBntQvyY2/2gcLX8rkPFfDDTQVF8qbRcRMVPLxBvFVJyfSAYRNch6ZyVH2GI6dtgALOBDpdNA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -2401,9 +2291,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.13.tgz",
|
||||
"integrity": "sha512-/W1+Q6tBAVgZWh/bhfOHo4n7Ryh6E7zYj4bJd9SRbkPyLtRioyK3bi6RLuDj57sa7Amk/DeomSV9iycS0xqIPA==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.14.tgz",
|
||||
"integrity": "sha512-bwDWLBalXFMDItcSXzFk6y7QKvj6oFlaY9vM+agTlwFL1n1OhDHYLZkSjaYsh6KCeG0VB0r7H8PUJVOM1LRZyg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2418,9 +2308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.13.tgz",
|
||||
"integrity": "sha512-GQj6TWevNxwsYw20FdT2r2d1f7uiRsF07iFvNYxPIvIyPEV74eZ0zgFEsAH1daK1OxPy+LXdZ4grV17P5tVzhQ==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.14.tgz",
|
||||
"integrity": "sha512-gVkJdnR/L6iIcGYXx64HGJRmlme2FGr/aZH0W6u4A3RgPMAb+6ELRLi+UBiH83RXBm9vwCfkIC/q8T51h8vUJQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2435,9 +2325,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.13.tgz",
|
||||
"integrity": "sha512-sQRH09faifF9w9WS6TKDWr1oLi4hoPx0EIWXZHQK/jcjarDpXGQ2DbF0KnALJCwWBxOIP/1nrmU01fZwwMzY3g==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.14.tgz",
|
||||
"integrity": "sha512-EE+EQ+c6tTpzsg+LGO1uuusjXxYx0Q00JE5ubcIGfsogSKth8n8i2BcS2wYTQe4jXGs+BQs35l78BIPzgwLddw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2452,9 +2342,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.13.tgz",
|
||||
"integrity": "sha512-Or1N8DIF3tP+LsloJp+UXLTIMMHMUcWXFhJLCsM4T7MzFzxkeReewRWXfk5mk137cdqVeUEH/R50xAhY1mOkTQ==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.14.tgz",
|
||||
"integrity": "sha512-KCCOzo+L6XPT0oUp2Jwh233ETRQ/F6cwUnMnR0FvMUCbkDAzHbcyOgpfuAtRa5HD0WbTbH4pVD+S0pn1EhNfbw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2469,9 +2359,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.13.tgz",
|
||||
"integrity": "sha512-u2mQyqCFrr9vVTP6sfDRfGE6bhOX3/7rInehzxNhHX1HYRIx09H3sDdXzTxnZWKOjIg3qjFTCrYFUZckva5PIg==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.14.tgz",
|
||||
"integrity": "sha512-AHObFiFL9lNYcm3tZSPqa/cHGpM5wOrNmM2uOMoKppp+0Hom5uuyRh0QkOp7jftsHZdrZUpmoz0Mp6vhh2XtUg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -2486,9 +2376,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.13.tgz",
|
||||
"integrity": "sha512-sOEc4iCanp1Yqyeu9suQcEzfaUcHnqjBUgDg0ZXpjUMUwdSi37S1lu1RGoV1BYInvvGu3y3HHTmvsSfDhx2L8w==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.14.tgz",
|
||||
"integrity": "sha512-rNXXMDJfCJLw/ZaFTOLOHoGULxyXfh2iXTGiChFiYTSgKBKQHIGEpV0yn5N25WGzJJ+VBnRjHzlmDqRV+d//oQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -2503,16 +2393,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.13.tgz",
|
||||
"integrity": "sha512-0XTd/NoVUAktIDaA4MdXhve0QWYh7WlZg20EHCuBFR80F8FhbVkRX+AY5cjbUP/IO2itHzt0iHc0iSE5kBUMhQ==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.14.tgz",
|
||||
"integrity": "sha512-y69ztPTRFy+13EPS/7dEFVl7q2Goh1pQueVO8IfGeyqSpcx/joNJXFk0lLhMgUbF0VFJotwRSb9ZY7Xoq3r26Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.0.13",
|
||||
"@tailwindcss/oxide": "4.0.13",
|
||||
"@tailwindcss/node": "4.0.14",
|
||||
"@tailwindcss/oxide": "4.0.14",
|
||||
"lightningcss": "1.29.2",
|
||||
"tailwindcss": "4.0.13"
|
||||
"tailwindcss": "4.0.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.2.0 || ^6"
|
||||
@ -3258,22 +3148,37 @@
|
||||
}
|
||||
},
|
||||
"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": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-draft-04": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
|
||||
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
||||
@ -3292,30 +3197,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
@ -3391,9 +3272,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bits-ui": {
|
||||
"version": "1.3.11",
|
||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.3.11.tgz",
|
||||
"integrity": "sha512-E8OMM9ae3iTwzKBdy6v0A9sp6xwvWEBz41TS5KkLYeFwZZnTJvc6P+0IqyzFLpQHxuRCZZNFLb5Wid00QvRbJA==",
|
||||
"version": "1.3.12",
|
||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.3.12.tgz",
|
||||
"integrity": "sha512-RhPvg2e7mTQSXR9WMdsyR5eTC2DSa4ch5MT/TjIaN3suMJ3RGlbzYPNvf4n56Lgck3W4Xm+L4jSgrzTdynuM8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -3943,9 +3824,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-svelte": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.1.0.tgz",
|
||||
"integrity": "sha512-hSQyLDkuuHPJby1ixZfUVrfLON42mT0Odf18MbwAgFUPuyIwJlhy3acUY1/bxt+Njucq/dQxR543zYDqkBNLmw==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.2.0.tgz",
|
||||
"integrity": "sha512-cxRkiF5XrydmLertOWoJ2LPqu0DItX3bbbJRFqzZ9MsRy3+3tPtQUsfsSuSxFTyDWWWAT1errzNhDLHehir9bw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4006,6 +3887,30 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esm-env": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||
@ -4917,9 +4822,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -6213,19 +6118,6 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@ -6292,9 +6184,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.9",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz",
|
||||
"integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==",
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.4.tgz",
|
||||
"integrity": "sha512-GTFcMIDgR7tqji/LpSY8rtg464VnJl/j6ypoehYnuGb+Y8qZUdtKB8WVCXon0UEZgFDbuUxpIl//6FHLHgXSNA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -6304,10 +6196,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
@ -6444,15 +6336,13 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
@ -6595,6 +6485,25 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.10",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.10.tgz",
|
||||
"integrity": "sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -6805,25 +6714,6 @@
|
||||
"vue": ">= 3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/radix-vue/node_modules/nanoid": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz",
|
||||
"integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
@ -7297,9 +7187,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.23.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.23.0.tgz",
|
||||
"integrity": "sha512-v0lL3NuKontiCxholEiAXCB+BYbndlKbwlDMK0DS86WgGELMJSpyqCSbJeMEMBDwOglnS7Ar2Rq0wwa/z2L8Vg==",
|
||||
"version": "5.23.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.23.1.tgz",
|
||||
"integrity": "sha512-DUu3e5tQDO+PtKffjqJ548YfeKtw2Rqc9/+nlP26DZ0AopWTJNylkNnTOP/wcgIt1JSnovyISxEZ/lDR1OhbOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -7347,9 +7237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-eslint-parser": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.0.1.tgz",
|
||||
"integrity": "sha512-JjdEMXOJqy+dxeaElxbN+meTOtVpHfLnq9VGpiTAOLgM0uHO+ogmUsA3IFgx0x3Wl15pqTZWycCikcD7cAQN/g==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.1.0.tgz",
|
||||
"integrity": "sha512-JP0v/wzDXWxza6c8K9ZjKKHYfgt0KidlbWx1e9n9UV4q+o28GTkk71fR0IDZDmLUDYs3vSq0+Tm9fofDqzGe1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -7425,9 +7315,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.13.tgz",
|
||||
"integrity": "sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q==",
|
||||
"version": "4.0.14",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.14.tgz",
|
||||
"integrity": "sha512-92YT2dpt671tFiHH/e1ok9D987N9fHD5VWoly1CdPD/Cd1HMglvZwP3nx2yTj2lbXDAHt8QssZkxTLCCTNL+xw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -7527,9 +7417,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tw-animate-css": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.0.tgz",
|
||||
"integrity": "sha512-bpQKhkp5CrI3GJ9IIHfhKjp2Fk1I43iGrJUvPadCdddr2oYBUXa+9o+zTRbJhylgbqosDtL97ss8PLTTcdAh8w==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.2.tgz",
|
||||
"integrity": "sha512-TdSaQcV+V8MypECoXr6Nnv3EQFz05kxfTUaOHtpWTQZp8r5Bx2OmTnkSZVlOTaRiS20a6wYw0RaAnNX2D8ywrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -7799,9 +7689,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz",
|
||||
"integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
|
||||
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
5
client/src/lib/sharedState.svelte.ts
Normal file
5
client/src/lib/sharedState.svelte.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { User } from "./services/user/v1/user_pb"
|
||||
|
||||
export let userState: { user: User | undefined } = $state({
|
||||
user: undefined
|
||||
});
|
15
client/src/lib/ui/Avatar.svelte
Normal file
15
client/src/lib/ui/Avatar.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { userState } from '$lib/sharedState.svelte';
|
||||
import { Avatar } from 'bits-ui';
|
||||
</script>
|
||||
|
||||
<Avatar.Root class="flex h-full w-full items-center justify-center">
|
||||
<Avatar.Image
|
||||
src={userState.user?.profilePicture}
|
||||
alt={`${userState.user?.username}'s avatar`}
|
||||
class="rounded-full"
|
||||
/>
|
||||
<Avatar.Fallback class="font-medium uppercase"
|
||||
>{userState.user?.username.substring(0, 2)}</Avatar.Fallback
|
||||
>
|
||||
</Avatar.Root>
|
@ -20,7 +20,7 @@
|
||||
<Button.Root
|
||||
{type}
|
||||
class={cn(
|
||||
'bg-sky text-crust hover:brightness-120 w-fit cursor-pointer rounded p-2 px-4 text-sm font-medium transition-all',
|
||||
'bg-sky text-crust flex justify-center items-center hover:brightness-120 focus:outline-sky w-fit cursor-pointer rounded p-2 px-4 text-sm font-medium transition-all focus:outline-2 focus:outline-offset-1',
|
||||
className
|
||||
)}
|
||||
{onclick}
|
||||
|
@ -1,23 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { ArrowLeft, ArrowRight, Minus, Calendar } from '@lucide/svelte';
|
||||
import { ArrowLeft, ArrowRight, Minus, Calendar, X } from '@lucide/svelte';
|
||||
import { DateRangePicker, type DateRange } from 'bits-ui';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { getLocalTimeZone } from '@internationalized/date';
|
||||
import { cn } from '$lib/utils';
|
||||
|
||||
let {
|
||||
className,
|
||||
start = $bindable(),
|
||||
end = $bindable(),
|
||||
onchange
|
||||
}: {
|
||||
className?: string;
|
||||
start?: Date;
|
||||
end?: Date;
|
||||
onchange?: (start: Date, end: Date) => void;
|
||||
onchange?: (start?: Date, end?: Date) => void;
|
||||
} = $props();
|
||||
|
||||
let daterange: DateRange | undefined = $state();
|
||||
let daterange: DateRange = $state({
|
||||
start: undefined,
|
||||
end: undefined
|
||||
});
|
||||
let rerender = $state(false);
|
||||
</script>
|
||||
|
||||
<DateRangePicker.Root
|
||||
<!-- Need to rerender because setting to undefined doesn't work -->
|
||||
{#key rerender}
|
||||
<DateRangePicker.Root
|
||||
bind:value={daterange}
|
||||
onValueChange={(v) => {
|
||||
if (v.start && v.end) {
|
||||
@ -28,11 +37,12 @@
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="bg-mantle border-surface-0 hover:border-surface-2 flex items-center justify-center gap-2 rounded border p-1 text-sm drop-shadow-md transition-all"
|
||||
class={cn(className)}
|
||||
>
|
||||
<DateRangePicker.Label />
|
||||
<div
|
||||
class="bg-mantle border-surface-0 hover:border-surface-2 flex items-center rounded border pl-2 text-sm drop-shadow-md transition-all"
|
||||
>
|
||||
<div class="grow flex items-center justify-center">
|
||||
{#each ['start', 'end'] as const as type}
|
||||
<DateRangePicker.Input {type}>
|
||||
{#snippet children({ segments })}
|
||||
@ -55,16 +65,34 @@
|
||||
{/snippet}
|
||||
</DateRangePicker.Input>
|
||||
{#if type === 'start'}
|
||||
<div aria-hidden="true">
|
||||
<div aria-hidden="true" class="px-1">
|
||||
<Minus size="10" />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<DateRangePicker.Trigger
|
||||
class="text-overlay-2 hover:bg-surface-0 ml-1 cursor-pointer rounded p-1 transition-all"
|
||||
class="text-overlay-2 hover:bg-surface-0 grow flex justify-center items-center focus:outline-sky ml-1 cursor-pointer p-2 transition-all focus:outline focus:outline-offset-1"
|
||||
>
|
||||
<Calendar size="20" />
|
||||
</DateRangePicker.Trigger>
|
||||
<button
|
||||
class="text-overlay-2 hover:bg-surface-0 focus:outline-sky cursor-pointer rounded-r p-2 transition-all focus:outline focus:outline-offset-1"
|
||||
onclick={() => {
|
||||
if (daterange) {
|
||||
daterange.end = undefined;
|
||||
daterange.start = undefined;
|
||||
}
|
||||
start = undefined;
|
||||
end = undefined;
|
||||
if (onchange) {
|
||||
onchange(start, end);
|
||||
}
|
||||
rerender = !rerender;
|
||||
}}
|
||||
>
|
||||
<X size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<DateRangePicker.Content forceMount>
|
||||
{#snippet child({ props, open })}
|
||||
@ -138,4 +166,5 @@
|
||||
{/if}
|
||||
{/snippet}
|
||||
</DateRangePicker.Content>
|
||||
</DateRangePicker.Root>
|
||||
</DateRangePicker.Root>
|
||||
{/key}
|
||||
|
51
client/src/lib/ui/Input.svelte
Normal file
51
client/src/lib/ui/Input.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
import { X } from '@lucide/svelte';
|
||||
|
||||
let {
|
||||
name,
|
||||
value = $bindable(''),
|
||||
type = 'text',
|
||||
placeholder,
|
||||
className,
|
||||
onchange
|
||||
}: {
|
||||
name?: string;
|
||||
value?: string | number;
|
||||
type?: string;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
onchange?: (e: Event) => void;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn(
|
||||
'border-surface-0 hover:border-surface-2 flex items-center justify-between gap-1 rounded border p-0 drop-shadow-md transition-all',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<input
|
||||
id={name}
|
||||
{name}
|
||||
{type}
|
||||
{placeholder}
|
||||
class="focus:outline-sky grow rounded-l p-2 text-sm transition-all focus:outline focus:outline-offset-1"
|
||||
bind:value
|
||||
{onchange}
|
||||
/>
|
||||
<button
|
||||
class="text-overlay-2 hover:bg-surface-0 focus:outline-sky cursor-pointer rounded-r p-2 transition-all focus:outline focus:outline-offset-1"
|
||||
type="button"
|
||||
onclick={(e) => {
|
||||
if (value) {
|
||||
value = '';
|
||||
if (onchange) {
|
||||
onchange(e);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<X size="20" />
|
||||
</button>
|
||||
</div>
|
@ -1,18 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { X } from '@lucide/svelte';
|
||||
import { Dialog } from 'bits-ui';
|
||||
import { fade } from 'svelte/transition';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
trigger,
|
||||
title,
|
||||
content,
|
||||
open = $bindable(false)
|
||||
}: { trigger: Snippet; content: Snippet; open?: boolean } = $props();
|
||||
}: {
|
||||
trigger: Snippet<[Record<string, unknown>]>;
|
||||
title: Snippet;
|
||||
content: Snippet;
|
||||
open?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open>
|
||||
<Dialog.Trigger>
|
||||
{@render trigger()}
|
||||
{#snippet child({ props })}
|
||||
{@render trigger(props)}
|
||||
{/snippet}
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay forceMount>
|
||||
@ -30,8 +39,8 @@
|
||||
{/snippet}
|
||||
</Dialog.Overlay>
|
||||
<Dialog.Content forceMount>
|
||||
{#snippet child({ props, open })}
|
||||
{#if open}
|
||||
{#snippet child({ props, open: propopen })}
|
||||
{#if propopen}
|
||||
<div
|
||||
{...props}
|
||||
transition:fade={{
|
||||
@ -41,6 +50,20 @@
|
||||
<div
|
||||
class="bg-mantle border-surface-0 fixed inset-0 left-[50%] top-[50%] z-50 size-fit w-96 -translate-x-1/2 -translate-y-1/2 transform overflow-y-auto rounded-xl border pb-1 drop-shadow-md"
|
||||
>
|
||||
<div class="border-surface-0 flex justify-between border-b p-2">
|
||||
<h1 class="grow truncate p-1 text-center text-xl font-bold">
|
||||
{@render title()}
|
||||
</h1>
|
||||
<button
|
||||
tabindex="-1"
|
||||
class="text-overlay-2 hover:bg-surface-0 focus:outline-sky cursor-pointer rounded p-1 transition-all focus:outline focus:outline-offset-1"
|
||||
onclick={() => {
|
||||
open = false;
|
||||
}}
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
{@render content()}
|
||||
</div>
|
||||
</div>
|
||||
|
63
client/src/lib/ui/Pagination.svelte
Normal file
63
client/src/lib/ui/Pagination.svelte
Normal file
@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
||||
import { Pagination } from 'bits-ui';
|
||||
|
||||
let {
|
||||
count = $bindable(),
|
||||
limit = $bindable(),
|
||||
offset = $bindable(0),
|
||||
className,
|
||||
onchange
|
||||
}: {
|
||||
count: number;
|
||||
limit: number;
|
||||
offset?: number;
|
||||
className?: string;
|
||||
onchange?: (e: number) => void;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
{#key count && limit}
|
||||
<Pagination.Root
|
||||
{count}
|
||||
perPage={limit}
|
||||
onPageChange={(e) => {
|
||||
offset = (e - 1) * limit;
|
||||
window.scrollTo(0, 0);
|
||||
onchange?.(e);
|
||||
}}
|
||||
>
|
||||
{#snippet children({ pages, range })}
|
||||
<div class={cn('mb-2 flex items-center justify-center gap-2', className)}>
|
||||
<Pagination.PrevButton
|
||||
class="hover:bg-surface-0 disabled:text-overlay-0 inline-flex cursor-pointer items-center justify-center rounded p-2 transition-all disabled:cursor-not-allowed hover:disabled:bg-transparent"
|
||||
>
|
||||
<ChevronLeft />
|
||||
</Pagination.PrevButton>
|
||||
<div class="flex items-center gap-2">
|
||||
{#each pages as page (page.key)}
|
||||
{#if page.type === 'ellipsis'}
|
||||
<div class="select-none font-medium">...</div>
|
||||
{:else}
|
||||
<Pagination.Page
|
||||
{page}
|
||||
class="hover:bg-surface-0 data-selected:bg-surface-0 data-selected:text-background inline-flex size-10 cursor-pointer select-none items-center justify-center rounded bg-transparent font-medium transition-all disabled:cursor-not-allowed disabled:opacity-50 hover:disabled:bg-transparent"
|
||||
>
|
||||
{page.value}
|
||||
</Pagination.Page>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<Pagination.NextButton
|
||||
class="hover:bg-surface-0 disabled:text-overlay-0 inline-flex cursor-pointer items-center justify-center rounded p-2 transition-all disabled:cursor-not-allowed hover:disabled:bg-transparent"
|
||||
>
|
||||
<ChevronRight />
|
||||
</Pagination.NextButton>
|
||||
</div>
|
||||
<p class="text-overlay-2 text-center text-sm">
|
||||
Showing {range.start} - {range.end}
|
||||
</p>
|
||||
{/snippet}
|
||||
</Pagination.Root>
|
||||
{/key}
|
82
client/src/lib/ui/Select.svelte
Normal file
82
client/src/lib/ui/Select.svelte
Normal file
@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils';
|
||||
import { Check, ChevronsDown, ChevronsUp, ChevronsUpDown, X } from '@lucide/svelte';
|
||||
import { Select } from 'bits-ui';
|
||||
|
||||
let {
|
||||
value = $bindable('10'),
|
||||
placeholder = 'Select an item',
|
||||
items = [],
|
||||
defaultValue = '',
|
||||
className,
|
||||
onchange
|
||||
}: {
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
items: { value: string; label: string; disabled?: boolean }[];
|
||||
defaultValue?: string;
|
||||
className?: string;
|
||||
onchange?: (e: string) => void;
|
||||
} = $props();
|
||||
|
||||
const selectedLabel = $derived(value ? items.find((i) => i.value === value)?.label : placeholder);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn(
|
||||
'border-surface-0 bg-mantle hover:border-surface-2 flex items-center justify-between rounded border p-0 drop-shadow-md transition-all',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Select.Root type="single" {items} bind:value onValueChange={onchange}>
|
||||
<Select.Trigger
|
||||
class="focus:outline-sky data-placeholder:text-overlay-0 gap-2 inline-flex grow cursor-pointer select-none items-center justify-between rounded-l py-2 pl-2 text-sm transition-colors focus:outline focus:outline-offset-1"
|
||||
aria-label={placeholder}
|
||||
>
|
||||
{selectedLabel}
|
||||
<ChevronsUpDown class="text-overlay-0" size="20" />
|
||||
</Select.Trigger>
|
||||
<Select.Portal>
|
||||
<Select.Content
|
||||
class="focus-override border-surface-0 bg-mantle shadow-popover data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 outline-hidden z-50 select-none rounded border p-1"
|
||||
sideOffset={10}
|
||||
>
|
||||
<Select.ScrollUpButton class="flex w-full items-center justify-center">
|
||||
<ChevronsUp size="20" />
|
||||
</Select.ScrollUpButton>
|
||||
<Select.Viewport class="p-1">
|
||||
{#each items as item, i (i + item.value)}
|
||||
<Select.Item
|
||||
class="data-disabled:cursor-not-allowed data-highlighted:bg-surface-0 outline-hidden data-disabled:opacity-50 flex h-10 w-full cursor-pointer select-none items-center gap-4 rounded px-5 py-3 text-sm capitalize"
|
||||
value={item.value}
|
||||
label={item.label}
|
||||
disabled={item.disabled}
|
||||
>
|
||||
{#snippet children({ selected })}
|
||||
{item.label}
|
||||
{#if selected}
|
||||
<div class="ml-auto">
|
||||
<Check size="20" />
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Viewport>
|
||||
<Select.ScrollDownButton class="flex w-full items-center justify-center">
|
||||
<ChevronsDown size="20" />
|
||||
</Select.ScrollDownButton>
|
||||
</Select.Content>
|
||||
</Select.Portal>
|
||||
</Select.Root>
|
||||
<button
|
||||
class="text-overlay-2 hover:bg-surface-0 focus:outline-sky cursor-pointer rounded-r p-2 transition-all focus:outline focus:outline-offset-1"
|
||||
type="button"
|
||||
onclick={() => {
|
||||
value = defaultValue;
|
||||
onchange?.(value);
|
||||
}}
|
||||
>
|
||||
<X size="20" />
|
||||
</button>
|
||||
</div>
|
@ -9,17 +9,20 @@
|
||||
House,
|
||||
type Icon as IconType
|
||||
} from '@lucide/svelte';
|
||||
import { NavigationMenu, Popover, Separator, Dialog, Avatar } from 'bits-ui';
|
||||
import { NavigationMenu, Popover, Separator, Dialog } from 'bits-ui';
|
||||
import { fade, fly, slide } from 'svelte/transition';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
import { AuthClient, UserClient } from '$lib/transport';
|
||||
import { page } from '$app/state';
|
||||
import { cn } from '$lib/utils';
|
||||
import { userState } from '$lib/sharedState.svelte';
|
||||
import Avatar from '$lib/ui/Avatar.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
let user = UserClient.getUser({}).then((res) => {
|
||||
return res.user;
|
||||
UserClient.getUser({}).then((res) => {
|
||||
userState.user = res.user;
|
||||
});
|
||||
|
||||
let sidebarOpen = $state(false);
|
||||
@ -52,6 +55,7 @@
|
||||
await AuthClient.logout({});
|
||||
await goto('/auth');
|
||||
toast.success('logged out successfully');
|
||||
userState.user = undefined;
|
||||
|
||||
if (sidebarOpen) {
|
||||
sidebarOpen = false;
|
||||
@ -175,16 +179,9 @@
|
||||
|
||||
<Popover.Root bind:open={popupOpen}>
|
||||
<Popover.Trigger
|
||||
class="outline-surface-2 hover:brightness-120 bg-text text-crust h-9 w-9 cursor-pointer rounded-full outline outline-offset-2 text-sm transition-all"
|
||||
class="outline-surface-2 hover:brightness-120 bg-text text-crust h-9 w-9 cursor-pointer rounded-full text-sm outline outline-offset-2 transition-all"
|
||||
>
|
||||
{#await user then user}
|
||||
<Avatar.Root class="flex h-full w-full items-center justify-center">
|
||||
<Avatar.Image src={user?.profilePicture} alt={`${user?.username}'s avatar`} class="rounded-full" />
|
||||
<Avatar.Fallback class="font-medium uppercase"
|
||||
>{user?.username.substring(0, 2)}</Avatar.Fallback
|
||||
>
|
||||
</Avatar.Root>
|
||||
{/await}
|
||||
<Avatar />
|
||||
</Popover.Trigger>
|
||||
<Popover.Content forceMount>
|
||||
{#snippet child({ wrapperProps, props, open })}
|
||||
@ -223,6 +220,6 @@
|
||||
</Popover.Root>
|
||||
</header>
|
||||
|
||||
<div class="pt-[50px] overflow-auto">
|
||||
<div class="overflow-auto pt-[50px]">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
@ -1,19 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { ItemClient } from '$lib/transport';
|
||||
import { Plus, Trash, Pencil, Calendar, Minus, ArrowLeft, ArrowRight } from '@lucide/svelte';
|
||||
import { Plus, Trash, Pencil } from '@lucide/svelte';
|
||||
import { timestampFromDate, timestampDate } from '@bufbuild/protobuf/wkt';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { ConnectError } from '@connectrpc/connect';
|
||||
import Modal from '$lib/ui/Modal.svelte';
|
||||
import Button from '$lib/ui/Button.svelte';
|
||||
import DateRangePicker from '$lib/ui/DateRangePicker.svelte';
|
||||
import Input from '$lib/ui/Input.svelte';
|
||||
import Select from '$lib/ui/Select.svelte';
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import type { Item } from '$lib/services/item/v1/item_pb';
|
||||
import Pagination from '$lib/ui/Pagination.svelte';
|
||||
|
||||
// Config
|
||||
let limit: number = $state(10);
|
||||
let offset: number = $state(0);
|
||||
let start = $state(new Date(new Date().setDate(new Date().getDate() - 1)));
|
||||
let end = $state(new Date());
|
||||
let start: Date | undefined = $state();
|
||||
let end: Date | undefined = $state();
|
||||
let filter = $state('');
|
||||
|
||||
// Items
|
||||
@ -29,8 +33,8 @@
|
||||
return await ItemClient.getItems({
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
start: timestampFromDate(start),
|
||||
end: timestampFromDate(end),
|
||||
start: start ? timestampFromDate(start) : undefined,
|
||||
end: end ? timestampFromDate(end) : undefined,
|
||||
filter: filter
|
||||
}).then((resp) => {
|
||||
count = Number(resp.count);
|
||||
@ -45,52 +49,44 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mx-4 my-2 flex items-center justify-center gap-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
class="border-surface-0 hover:border-surface-2 w-70 bg-mantle rounded border p-2 text-sm drop-shadow-md transition-all"
|
||||
<div class="mx-4 my-2 flex flex-wrap items-center justify-center gap-2">
|
||||
<Input
|
||||
bind:value={filter}
|
||||
className="bg-mantle"
|
||||
placeholder="Filter"
|
||||
onchange={updateItems}
|
||||
/>
|
||||
<DateRangePicker bind:start bind:end />
|
||||
<Select
|
||||
items={[
|
||||
{
|
||||
label: '10 Items',
|
||||
value: '10'
|
||||
},
|
||||
{
|
||||
label: '25 Items',
|
||||
value: '25'
|
||||
},
|
||||
{
|
||||
label: '100 Items',
|
||||
value: '100'
|
||||
},
|
||||
{
|
||||
label: '250 Items',
|
||||
value: '250'
|
||||
}
|
||||
]}
|
||||
placeholder="Items per page"
|
||||
onchange={() => {
|
||||
offset = 0;
|
||||
updateItems();
|
||||
}}
|
||||
defaultValue="10"
|
||||
bind:value={() => limit.toString(), (v) => (limit = parseInt(v))}
|
||||
/>
|
||||
<DateRangePicker bind:start bind:end onchange={updateItems} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="border-surface-0 bg-mantle mx-4 my-2 overflow-x-auto rounded border-x border-t drop-shadow-md"
|
||||
>
|
||||
<table class="w-full table-auto border-collapse text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr class="border-surface-0 border-b">
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Added</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Name</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Description</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Price</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Quantity</th>
|
||||
<th class="w-0"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#await items}
|
||||
<tr class="border-surface-0 border-b">
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="w-8"></td>
|
||||
</tr>
|
||||
{:then items}
|
||||
{#each items as item}
|
||||
<tr class="border-surface-0 border-b">
|
||||
<td class="px-6 py-3">
|
||||
{item.added ? timestampDate(item.added).toLocaleString() : ''}
|
||||
</td>
|
||||
<td class="px-6 py-3">{item.name}</td>
|
||||
<td class="px-6 py-3">{item.description}</td>
|
||||
<td class="px-6 py-3">{item.price}</td>
|
||||
<td class="px-6 py-3">{item.quantity}</td>
|
||||
<td class="pr-2">
|
||||
<div class="flex gap-2">
|
||||
{#snippet editModal(item: Item)}
|
||||
<Modal
|
||||
bind:open={
|
||||
() =>
|
||||
@ -100,16 +96,17 @@
|
||||
(value) => editsOpen.set(item.id!, value)
|
||||
}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button className="bg-text">
|
||||
{#snippet trigger(props)}
|
||||
<Button {...props} className="bg-text">
|
||||
<Pencil />
|
||||
</Button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet title()}
|
||||
Edit '{item.name}'
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
|
||||
Edit {item.name}
|
||||
</h1>
|
||||
<form
|
||||
onsubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
@ -145,50 +142,28 @@
|
||||
<div class="flex flex-col gap-4 p-3">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="name" class="text-sm">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
value={item.name}
|
||||
/>
|
||||
<Input name="name" type="text" value={item.name} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="description" class="text-sm">Description</label>
|
||||
<input
|
||||
id="description"
|
||||
name="description"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
value={item.description}
|
||||
/>
|
||||
<Input name="description" type="text" value={item.description} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="price" class="text-sm">Price</label>
|
||||
<input
|
||||
id="price"
|
||||
name="price"
|
||||
type="number"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
value={item.price}
|
||||
/>
|
||||
<Input name="price" type="number" value={item.price} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="quantity" class="text-sm">Quantity</label>
|
||||
<input
|
||||
id="quantity"
|
||||
name="quantity"
|
||||
type="number"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
value={item.quantity}
|
||||
/>
|
||||
<Input name="quantity" type="number" value={item.quantity} />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
</form>
|
||||
{/snippet}
|
||||
</Modal>
|
||||
{/snippet}
|
||||
|
||||
{#snippet deleteModal(item: Item)}
|
||||
<Modal
|
||||
bind:open={
|
||||
() =>
|
||||
@ -198,16 +173,17 @@
|
||||
(value) => deletesOpen.set(item.id!, value)
|
||||
}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button className="bg-red">
|
||||
{#snippet trigger(props)}
|
||||
<Button {...props} className="bg-red">
|
||||
<Trash />
|
||||
</Button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet title()}
|
||||
Delete '{item.name}'
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
|
||||
Delete {item.name}
|
||||
</h1>
|
||||
<form
|
||||
onsubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
@ -227,9 +203,7 @@
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col gap-4 p-3">
|
||||
<span class="text-center"
|
||||
>Are you sure you want to delete "{item.name}"?</span
|
||||
>
|
||||
<span class="text-center">Are you sure you want to delete "{item.name}"?</span>
|
||||
<div class="flex justify-center gap-4">
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
@ -237,6 +211,46 @@
|
||||
</form>
|
||||
{/snippet}
|
||||
</Modal>
|
||||
{/snippet}
|
||||
|
||||
<div
|
||||
class="border-surface-0 bg-mantle mx-4 my-2 hidden overflow-x-auto rounded border-x border-t drop-shadow-md sm:block"
|
||||
>
|
||||
<table class="w-full table-auto border-collapse text-left rtl:text-right">
|
||||
<thead>
|
||||
<tr class="border-surface-0 border-b">
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Added</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Name</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Description</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Price</th>
|
||||
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Quantity</th>
|
||||
<th class="w-0"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#await items}
|
||||
<tr class="border-surface-0 border-b">
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
|
||||
<td class="w-8"></td>
|
||||
</tr>
|
||||
{:then items}
|
||||
{#each items as item}
|
||||
<tr class="border-surface-0 border-b">
|
||||
<td class="px-6 py-3">
|
||||
{item.added ? timestampDate(item.added).toLocaleString() : ''}
|
||||
</td>
|
||||
<td class="px-6 py-3">{item.name}</td>
|
||||
<td class="px-6 py-3">{item.description}</td>
|
||||
<td class="px-6 py-3">${item.price}</td>
|
||||
<td class="px-6 py-3">{item.quantity}</td>
|
||||
<td class="pr-2">
|
||||
<div class="flex gap-2">
|
||||
{@render editModal(item)}
|
||||
{@render deleteModal(item)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -246,16 +260,81 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mx-4 mt-1 flex justify-end">
|
||||
<div class="flex flex-wrap justify-center gap-2 px-4 sm:hidden">
|
||||
{#await items}
|
||||
<div
|
||||
class="border-surface-0 bg-mantle flex w-full flex-col gap-2 rounded border p-5 drop-shadow-md"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Added</span>
|
||||
<div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Name</span>
|
||||
<div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Description</span>
|
||||
<div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Price</span>
|
||||
<div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Quantity</span>
|
||||
<div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
{:then items}
|
||||
{#each items as item}
|
||||
<div
|
||||
class="border-surface-0 bg-mantle flex w-full flex-wrap gap-6 rounded border p-5 drop-shadow-md"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Added</span>
|
||||
<span class="truncate"
|
||||
>{item.added ? timestampDate(item.added).toLocaleString() : ''}</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Name</span>
|
||||
<span class="truncate">{item.name}</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Description</span>
|
||||
<span class="truncate">{item.description}</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Price</span>
|
||||
<span class="truncate">${item.price}</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-subtext-0 text-sm">Quantity</span>
|
||||
<span class="truncate">{item.quantity}</span>
|
||||
</div>
|
||||
<div class="flex justify-end ml-auto gap-2">
|
||||
{@render editModal(item)}
|
||||
{@render deleteModal(item)}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
<div class="mx-4 mb-4 mt-2 flex justify-end sm:mt-1">
|
||||
<Modal bind:open={addedOpen}>
|
||||
{#snippet trigger()}
|
||||
<Button className="bg-sky">
|
||||
{#snippet trigger(props)}
|
||||
<Button {...props} className="bg-sky">
|
||||
<Plus />
|
||||
</Button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet title()}
|
||||
Add Item
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">Add Item</h1>
|
||||
<form
|
||||
onsubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
@ -291,39 +370,19 @@
|
||||
<div class="flex flex-col gap-4 p-3">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="name" class="text-sm">Name</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="name" type="text" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="description" class="text-sm">Description</label>
|
||||
<input
|
||||
id="description"
|
||||
name="description"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="description" type="text" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="price" class="text-sm">Price</label>
|
||||
<input
|
||||
id="price"
|
||||
name="price"
|
||||
type="number"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="price" type="text" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="quantity" class="text-sm">Quantity</label>
|
||||
<input
|
||||
id="quantity"
|
||||
name="quantity"
|
||||
type="number"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="quantity" type="text" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
@ -331,3 +390,7 @@
|
||||
{/snippet}
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
<div class="py-4">
|
||||
<Pagination bind:count bind:limit bind:offset onchange={updateItems} />
|
||||
</div>
|
||||
|
@ -2,46 +2,41 @@
|
||||
import { UserClient } from '$lib/transport';
|
||||
import Button from '$lib/ui/Button.svelte';
|
||||
import Modal from '$lib/ui/Modal.svelte';
|
||||
import Input from '$lib/ui/Input.svelte';
|
||||
import { ConnectError } from '@connectrpc/connect';
|
||||
import { Avatar, Separator } from 'bits-ui';
|
||||
import { Separator } from 'bits-ui';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { userState } from '$lib/sharedState.svelte';
|
||||
import Avatar from '$lib/ui/Avatar.svelte';
|
||||
|
||||
let user = UserClient.getUser({}).then((res) => {
|
||||
return res.user;
|
||||
});
|
||||
let openChangeProfilePicture = $state(false);
|
||||
let key = $state('');
|
||||
</script>
|
||||
|
||||
<div class="flex h-[calc(100vh-50px)]">
|
||||
<div class="m-auto flex w-96 flex-col gap-4 p-4">
|
||||
{#await user then user}
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<div
|
||||
class="outline-surface-2 bg-text text-crust h-9 w-9 select-none rounded-full outline outline-offset-2 text-sm"
|
||||
class="outline-surface-2 bg-text text-crust h-9 w-9 select-none rounded-full text-sm outline outline-offset-2"
|
||||
>
|
||||
<Avatar.Root class="flex h-full w-full items-center justify-center">
|
||||
<Avatar.Image src={user?.profilePicture} alt={`${user?.username}'s avatar`} class="rounded-full" />
|
||||
<Avatar.Fallback class="font-medium uppercase"
|
||||
>{user?.username.substring(0, 2)}</Avatar.Fallback
|
||||
>
|
||||
</Avatar.Root>
|
||||
<Avatar />
|
||||
</div>
|
||||
<h1 class="overflow-x-hidden text-2xl font-medium">{user?.username}</h1>
|
||||
<h1 class="overflow-x-hidden text-2xl font-medium">{userState.user?.username}</h1>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<Separator.Root class="bg-surface-0 h-px" />
|
||||
|
||||
<div class="flex justify-around gap-2">
|
||||
<Modal>
|
||||
{#snippet trigger()}
|
||||
<Button className="bg-text">Generate API Key</Button>
|
||||
{#snippet trigger(props)}
|
||||
<Button {...props} className="bg-text">Generate API Key</Button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet title()}
|
||||
Generate API Key
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
|
||||
Generate API Key
|
||||
</h1>
|
||||
{#if key == ''}
|
||||
<form
|
||||
onsubmit={async (e) => {
|
||||
@ -68,21 +63,11 @@
|
||||
<div class="flex flex-col gap-4 p-3">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="password" class="text-sm">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="password" type="password" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="confirm-password" class="text-sm">Confirm Password</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="confirm-password" type="password" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
@ -95,15 +80,16 @@
|
||||
{/snippet}
|
||||
</Modal>
|
||||
|
||||
<Modal>
|
||||
{#snippet trigger()}
|
||||
<Button className="bg-text">Change Profile Picture</Button>
|
||||
<Modal bind:open={openChangeProfilePicture}>
|
||||
{#snippet trigger(props)}
|
||||
<Button {...props} className="bg-text">Change Profile Picture</Button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet title()}
|
||||
Change Profile Picture
|
||||
{/snippet}
|
||||
|
||||
{#snippet content()}
|
||||
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
|
||||
Change Profile Picture
|
||||
</h1>
|
||||
<form
|
||||
onsubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
@ -122,13 +108,14 @@
|
||||
try {
|
||||
const response = await UserClient.updateProfilePicture({
|
||||
fileName: file.name,
|
||||
data: data,
|
||||
data: data
|
||||
});
|
||||
|
||||
if (response.user) {
|
||||
toast.success('Profile picture updated');
|
||||
form.reset();
|
||||
|
||||
openChangeProfilePicture = false;
|
||||
userState.user = response.user;
|
||||
}
|
||||
} catch (err) {
|
||||
const error = ConnectError.from(err);
|
||||
@ -139,12 +126,7 @@
|
||||
<div class="flex flex-col gap-4 p-3">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="file" class="text-sm">Profile Picture</label>
|
||||
<input
|
||||
id="file"
|
||||
name="file"
|
||||
type="file"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="file" type="file" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
@ -178,30 +160,15 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="old-password" class="text-sm">Old Password</label>
|
||||
<input
|
||||
id="old-password"
|
||||
name="old-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="old-password" type="password" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="new-password" class="text-sm">New Password</label>
|
||||
<input
|
||||
id="new-password"
|
||||
name="new-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="new-password" type="password" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="confirm-password" class="text-sm">Confirm New Password</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
name="confirm-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="confirm-password" type="password" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { ConnectError } from '@connectrpc/connect';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import Button from '$lib/ui/Button.svelte';
|
||||
import Input from '$lib/ui/Input.svelte';
|
||||
|
||||
let tab = $state('login');
|
||||
</script>
|
||||
@ -59,21 +60,11 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="login-username" class="text-sm">Username</label>
|
||||
<input
|
||||
id="login-username"
|
||||
name="login-username"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="login-username" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="login-password" class="text-sm">Password</label>
|
||||
<input
|
||||
id="login-password"
|
||||
name="login-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="login-password" type="password" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
@ -108,30 +99,15 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="signup-username" class="text-sm">Username</label>
|
||||
<input
|
||||
id="signup-username"
|
||||
name="signup-username"
|
||||
type="text"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="signup-username" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="signup-password" class="text-sm">Password</label>
|
||||
<input
|
||||
id="signup-password"
|
||||
name="signup-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="signup-password" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label for="signup-confirm-password" class="text-sm">Confirm Password</label>
|
||||
<input
|
||||
id="signup-confirm-password"
|
||||
name="signup-confirm-password"
|
||||
type="password"
|
||||
class="border-surface-0 rounded border p-2 text-sm"
|
||||
/>
|
||||
<Input name="signup-confirm-password" />
|
||||
</div>
|
||||
<Button type="submit">Submit</Button>
|
||||
</div>
|
||||
|
22
server/internal/database/logger.go
Normal file
22
server/internal/database/logger.go
Normal file
@ -0,0 +1,22 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func NewLogger() logger.Interface {
|
||||
return logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL threshold
|
||||
LogLevel: logger.Silent, // Log level
|
||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
ParameterizedQueries: true, // Don't include params in the SQL log
|
||||
Colorful: true, // Disable color
|
||||
},
|
||||
)
|
||||
}
|
@ -7,7 +7,9 @@ import (
|
||||
|
||||
func NewPostgresConnection(user, pass, host, port, name string) (*gorm.DB, error) {
|
||||
dsn := "host=" + host + " user=" + user + " password=" + pass + " dbname=" + name + " port=" + port + " sslmode=disable TimeZone=UTC"
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||
Logger: NewLogger(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ func NewSQLiteConnection(name string) (*gorm.DB, error) {
|
||||
|
||||
// Open database
|
||||
dbPath := filepath.Join(settingsPath, name)
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
Logger: NewLogger(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -47,6 +48,7 @@ func (h *ItemHandler) GetItems(ctx context.Context, req *connect.Request[itemv1.
|
||||
// Filters
|
||||
sql := h.db.Where("user_id = ?", userid)
|
||||
if req.Msg.Start != nil {
|
||||
log.Println(req.Msg.Start)
|
||||
sql = sql.Where("added >= ?", req.Msg.Start.AsTime())
|
||||
}
|
||||
if req.Msg.End != nil {
|
||||
|
@ -151,6 +151,7 @@ func (h *UserHandler) UpdateProfilePicture(ctx context.Context, req *connect.Req
|
||||
// Update user profile picture
|
||||
fid := uint(file.ID)
|
||||
user.ProfilePictureID = &fid
|
||||
user.ProfilePicture = &file
|
||||
if err := h.db.Save(&user).Error; err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user