From 42d90574bd098a429beba3dba2aad86fc1759d8b Mon Sep 17 00:00:00 2001 From: Jan Lauber Date: Sun, 30 Jun 2024 13:05:50 +0200 Subject: [PATCH] feat: enhance download of CSV for admin user Signed-off-by: Jan Lauber --- pb/pb_migrations/1719742575_updated_users.js | 33 ++ .../1719742705_updated_expenses.js | 18 + .../1719742711_updated_expenses.js | 18 + pb/pb_migrations/1719742738_updated_users.js | 48 +++ .../1719742767_updated_expenses.js | 18 + pb/pb_migrations/1719743138_updated_users.js | 18 + sk/package-lock.json | 218 +++++++++++- sk/package.json | 7 +- sk/src/app.html | 5 +- sk/src/grid.css | 326 ------------------ sk/src/lib/pocketbase/generated-types.ts | 1 + sk/src/lib/utils/file.utils.ts | 5 + sk/src/routes/+layout.svelte | 1 - sk/src/routes/app/+layout.svelte | 88 +++-- sk/src/routes/app/+page.svelte | 147 +++++++- sk/src/routes/app/analytics/+page.ts | 0 16 files changed, 575 insertions(+), 376 deletions(-) create mode 100644 pb/pb_migrations/1719742575_updated_users.js create mode 100644 pb/pb_migrations/1719742705_updated_expenses.js create mode 100644 pb/pb_migrations/1719742711_updated_expenses.js create mode 100644 pb/pb_migrations/1719742738_updated_users.js create mode 100644 pb/pb_migrations/1719742767_updated_expenses.js create mode 100644 pb/pb_migrations/1719743138_updated_users.js delete mode 100644 sk/src/grid.css create mode 100644 sk/src/lib/utils/file.utils.ts create mode 100644 sk/src/routes/app/analytics/+page.ts diff --git a/pb/pb_migrations/1719742575_updated_users.js b/pb/pb_migrations/1719742575_updated_users.js new file mode 100644 index 0000000..9345b96 --- /dev/null +++ b/pb/pb_migrations/1719742575_updated_users.js @@ -0,0 +1,33 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // add + collection.schema.addField(new SchemaField({ + "system": false, + "id": "61gndav1", + "name": "role", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "user", + "admin" + ] + } + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // remove + collection.schema.removeField("61gndav1") + + return dao.saveCollection(collection) +}) diff --git a/pb/pb_migrations/1719742705_updated_expenses.js b/pb/pb_migrations/1719742705_updated_expenses.js new file mode 100644 index 0000000..399c499 --- /dev/null +++ b/pb/pb_migrations/1719742705_updated_expenses.js @@ -0,0 +1,18 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")\n" + collection.viewRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")\n" + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && @request.auth.id = user.id" + collection.viewRule = "@request.auth.id != \"\" && @request.auth.id = user.id" + + return dao.saveCollection(collection) +}) diff --git a/pb/pb_migrations/1719742711_updated_expenses.js b/pb/pb_migrations/1719742711_updated_expenses.js new file mode 100644 index 0000000..30775f7 --- /dev/null +++ b/pb/pb_migrations/1719742711_updated_expenses.js @@ -0,0 +1,18 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")" + collection.viewRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")" + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")\n" + collection.viewRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")\n" + + return dao.saveCollection(collection) +}) diff --git a/pb/pb_migrations/1719742738_updated_users.js b/pb/pb_migrations/1719742738_updated_users.js new file mode 100644 index 0000000..34bb3ca --- /dev/null +++ b/pb/pb_migrations/1719742738_updated_users.js @@ -0,0 +1,48 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // remove + collection.schema.removeField("61gndav1") + + // add + collection.schema.addField(new SchemaField({ + "system": false, + "id": "ntdnwymg", + "name": "admin", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + })) + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + // add + collection.schema.addField(new SchemaField({ + "system": false, + "id": "61gndav1", + "name": "role", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "user", + "admin" + ] + } + })) + + // remove + collection.schema.removeField("ntdnwymg") + + return dao.saveCollection(collection) +}) diff --git a/pb/pb_migrations/1719742767_updated_expenses.js b/pb/pb_migrations/1719742767_updated_expenses.js new file mode 100644 index 0000000..088019c --- /dev/null +++ b/pb/pb_migrations/1719742767_updated_expenses.js @@ -0,0 +1,18 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.admin = true)" + collection.viewRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.admin = true)" + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("frv5fa0d678jb4c") + + collection.listRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")" + collection.viewRule = "@request.auth.id != \"\" && (@request.auth.id = user.id || @request.auth.role = \"admin\")" + + return dao.saveCollection(collection) +}) diff --git a/pb/pb_migrations/1719743138_updated_users.js b/pb/pb_migrations/1719743138_updated_users.js new file mode 100644 index 0000000..6ed1b36 --- /dev/null +++ b/pb/pb_migrations/1719743138_updated_users.js @@ -0,0 +1,18 @@ +/// +migrate((db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + collection.listRule = "id = @request.auth.id || @request.auth.admin = true" + collection.viewRule = "id = @request.auth.id || @request.auth.admin = true" + + return dao.saveCollection(collection) +}, (db) => { + const dao = new Dao(db) + const collection = dao.findCollectionByNameOrId("_pb_users_auth_") + + collection.listRule = "id = @request.auth.id" + collection.viewRule = "id = @request.auth.id" + + return dao.saveCollection(collection) +}) diff --git a/sk/package-lock.json b/sk/package-lock.json index b261ad2..7a5be15 100644 --- a/sk/package-lock.json +++ b/sk/package-lock.json @@ -17,12 +17,14 @@ "bits-ui": "^0.21.9", "clsx": "^2.1.1", "cmdk-sv": "^0.0.17", + "file-saver": "^2.0.5", "gridjs": "^6.2.0", "gridjs-svelte": "^2.1.1", "js-yaml": "^4.1.0", "lucide-svelte": "^0.379.0", "mode-watcher": "^0.3.0", "openapi-fetch": "^0.10.1", + "papaparse": "^5.4.1", "pocketbase": "^0.21.3", "svelte-french-toast": "^1.2.0", "svelte-legos": "^0.2.3", @@ -31,7 +33,9 @@ "tailwind-merge": "^2.3.0", "tailwind-variants": "^0.2.1", "tesseract.js": "^5.1.0", - "vaul-svelte": "^0.3.1" + "vaul-svelte": "^0.3.1", + "xlsx": "^0.18.5", + "xlsx-populate": "^1.21.0" }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.13.4", @@ -52,6 +56,7 @@ "svelte": "^4.2.16", "svelte-check": "^3.7.1", "svelte-headless-table": "^0.8.3", + "svelte-lightbox": "^1.1.3", "svelte-preprocess": "^5.1.4", "tailwindcss": "^3.4.3", "tslib": "^2.6.2", @@ -3724,6 +3729,14 @@ "node": ">=0.4.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4360,6 +4373,18 @@ "url": "https://www.paypal.me/kirilvatev" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4618,6 +4643,14 @@ "periscopic": "^3.1.0" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -4825,6 +4858,22 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -5499,6 +5548,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -5625,6 +5679,14 @@ "node": ">= 6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -6117,6 +6179,11 @@ "node": ">=18.0.0" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immutable": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", @@ -6202,8 +6269,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -6833,6 +6899,49 @@ "node": ">=0.10.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -6858,6 +6967,14 @@ "node": ">=6" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -6879,8 +6996,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.castarray": { "version": "4.4.0", @@ -7599,6 +7715,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8009,6 +8135,11 @@ "node": ">=6" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -8505,6 +8636,11 @@ "node": ">=14.0.0" } }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/semver": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -8574,6 +8710,11 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/sharp": { "version": "0.33.4", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.4.tgz", @@ -8920,6 +9061,17 @@ } } }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -9321,6 +9473,15 @@ "svelte": "^4.0.0" } }, + "node_modules/svelte-lightbox": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/svelte-lightbox/-/svelte-lightbox-1.1.3.tgz", + "integrity": "sha512-pPkNo89nUFEgOvIUZZA2CUSZpABp/LkR3vZz5gD+Sv0q1kmP0AcdQQquenuLyZcSigth0iqBFCd/bB3LSPMejg==", + "dev": true, + "peerDependencies": { + "svelte": "^3.25.0 || ^4.0.0" + } + }, "node_modules/svelte-parse-markup": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/svelte-parse-markup/-/svelte-parse-markup-0.1.4.tgz", @@ -10280,6 +10441,22 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/workbox-background-sync": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.1.0.tgz", @@ -10711,6 +10888,37 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx-populate": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/xlsx-populate/-/xlsx-populate-1.21.0.tgz", + "integrity": "sha512-8v2Gm8BehXo6LU7KT802QoXTPkYY1SKk5V8g/UuYZnNB3JzXqud/P99Pxr2yXeKyt+sKlCatmidz6jQNie1hRw==", + "dependencies": { + "cfb": "^1.1.3", + "jszip": "^3.2.2", + "lodash": "^4.17.15", + "sax": "^1.2.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/sk/package.json b/sk/package.json index 93bbf1e..c5e427d 100644 --- a/sk/package.json +++ b/sk/package.json @@ -35,6 +35,7 @@ "svelte": "^4.2.16", "svelte-check": "^3.7.1", "svelte-headless-table": "^0.8.3", + "svelte-lightbox": "^1.1.3", "svelte-preprocess": "^5.1.4", "tailwindcss": "^3.4.3", "tslib": "^2.6.2", @@ -52,12 +53,14 @@ "bits-ui": "^0.21.9", "clsx": "^2.1.1", "cmdk-sv": "^0.0.17", + "file-saver": "^2.0.5", "gridjs": "^6.2.0", "gridjs-svelte": "^2.1.1", "js-yaml": "^4.1.0", "lucide-svelte": "^0.379.0", "mode-watcher": "^0.3.0", "openapi-fetch": "^0.10.1", + "papaparse": "^5.4.1", "pocketbase": "^0.21.3", "svelte-french-toast": "^1.2.0", "svelte-legos": "^0.2.3", @@ -66,6 +69,8 @@ "tailwind-merge": "^2.3.0", "tailwind-variants": "^0.2.1", "tesseract.js": "^5.1.0", - "vaul-svelte": "^0.3.1" + "vaul-svelte": "^0.3.1", + "xlsx": "^0.18.5", + "xlsx-populate": "^1.21.0" } } diff --git a/sk/src/app.html b/sk/src/app.html index dec45a4..e085f20 100644 --- a/sk/src/app.html +++ b/sk/src/app.html @@ -3,10 +3,7 @@ - + %sveltekit.head% diff --git a/sk/src/grid.css b/sk/src/grid.css deleted file mode 100644 index f0c182d..0000000 --- a/sk/src/grid.css +++ /dev/null @@ -1,326 +0,0 @@ -.gridjs-footer button, -.gridjs-head button { - background-color: transparent; - background-image: none; - border: none; - cursor: pointer; - margin: 0; - outline: none; - padding: 0; -} -.gridjs-temp { - position: relative; -} -.gridjs-head { - margin-bottom: 5px; - padding: 5px 1px; - width: 100%; -} -.gridjs-head:after { - clear: both; - content: ""; - display: block; -} -.gridjs-head:empty { - border: none; - padding: 0; -} -.gridjs-container { - color: #000; - display: inline-block; - overflow: hidden; - padding: 2px; - position: relative; - z-index: 0; -} -.gridjs-footer { - background-color: #fff; - border-bottom-width: 1px; - border-color: #e5e7eb; - border-radius: 0 0 8px 8px; - border-top: 1px solid #e5e7eb; - box-shadow: - 0 1px 3px 0 rgba(0, 0, 0, 0.1), - 0 1px 2px 0 rgba(0, 0, 0, 0.26); - display: block; - padding: 12px 24px; - position: relative; - width: 100%; - z-index: 5; -} -.gridjs-footer:empty { - border: none; - padding: 0; -} -input.gridjs-input { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border: 1px solid #d2d6dc; - border-radius: 5px; - font-size: 14px; - line-height: 1.45; - outline: none; - padding: 10px 13px; -} -input.gridjs-input:focus { - border-color: #9bc2f7; - box-shadow: 0 0 0 3px rgba(149, 189, 243, 0.5); -} -.gridjs-pagination { - color: #3d4044; -} -.gridjs-pagination:after { - clear: both; - content: ""; - display: block; -} -.gridjs-pagination .gridjs-summary { - float: left; - margin-top: 5px; -} -.gridjs-pagination .gridjs-pages { - float: right; -} -.gridjs-pagination .gridjs-pages button { - background-color: #fff; - border: 1px solid #d2d6dc; - border-right: none; - outline: none; - padding: 5px 14px; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} -.gridjs-pagination .gridjs-pages button:focus { - border-right: 1px solid #d2d6dc; - box-shadow: 0 0 0 2px rgba(149, 189, 243, 0.5); - margin-right: -1px; - position: relative; -} -.gridjs-pagination .gridjs-pages button:hover { - background-color: #f7f7f7; - color: #3c4257; - outline: none; -} -.gridjs-pagination .gridjs-pages button:disabled, -.gridjs-pagination .gridjs-pages button:hover:disabled, -.gridjs-pagination .gridjs-pages button[disabled] { - background-color: #fff; - color: #6b7280; - cursor: default; -} -.gridjs-pagination .gridjs-pages button.gridjs-spread { - background-color: #fff; - box-shadow: none; - cursor: default; -} -.gridjs-pagination .gridjs-pages button.gridjs-currentPage { - background-color: #f7f7f7; - font-weight: 700; -} -.gridjs-pagination .gridjs-pages button:last-child { - border-bottom-right-radius: 6px; - border-right: 1px solid #d2d6dc; - border-top-right-radius: 6px; -} -.gridjs-pagination .gridjs-pages button:first-child { - border-bottom-left-radius: 6px; - border-top-left-radius: 6px; -} -.gridjs-pagination .gridjs-pages button:last-child:focus { - margin-right: 0; -} -button.gridjs-sort { - background-color: transparent; - background-position-x: center; - background-repeat: no-repeat; - background-size: contain; - border: none; - cursor: pointer; - float: right; - height: 24px; - margin: 0; - outline: none; - padding: 0; - width: 13px; -} -button.gridjs-sort-neutral { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDEuOTk4IiBoZWlnaHQ9IjQwMS45OTgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwMS45OTggNDAxLjk5OCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTczLjA5MiAxNjQuNDUyaDI1NS44MTNjNC45NDkgMCA5LjIzMy0xLjgwNyAxMi44NDgtNS40MjQgMy42MTMtMy42MTYgNS40MjctNy44OTggNS40MjctMTIuODQ3cy0xLjgxMy05LjIyOS01LjQyNy0xMi44NUwyMTMuODQ2IDUuNDI0QzIxMC4yMzIgMS44MTIgMjA1Ljk1MSAwIDIwMC45OTkgMHMtOS4yMzMgMS44MTItMTIuODUgNS40MjRMNjAuMjQyIDEzMy4zMzFjLTMuNjE3IDMuNjE3LTUuNDI0IDcuOTAxLTUuNDI0IDEyLjg1IDAgNC45NDggMS44MDcgOS4yMzEgNS40MjQgMTIuODQ3IDMuNjIxIDMuNjE3IDcuOTAyIDUuNDI0IDEyLjg1IDUuNDI0ek0zMjguOTA1IDIzNy41NDlINzMuMDkyYy00Ljk1MiAwLTkuMjMzIDEuODA4LTEyLjg1IDUuNDIxLTMuNjE3IDMuNjE3LTUuNDI0IDcuODk4LTUuNDI0IDEyLjg0N3MxLjgwNyA5LjIzMyA1LjQyNCAxMi44NDhMMTg4LjE0OSAzOTYuNTdjMy42MjEgMy42MTcgNy45MDIgNS40MjggMTIuODUgNS40MjhzOS4yMzMtMS44MTEgMTIuODQ3LTUuNDI4bDEyNy45MDctMTI3LjkwNmMzLjYxMy0zLjYxNCA1LjQyNy03Ljg5OCA1LjQyNy0xMi44NDggMC00Ljk0OC0xLjgxMy05LjIyOS01LjQyNy0xMi44NDctMy42MTQtMy42MTYtNy44OTktNS40Mi0xMi44NDgtNS40MnoiLz48L3N2Zz4="); - background-position-y: center; - opacity: 0.3; -} -button.gridjs-sort-asc { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIuMzYyIiBoZWlnaHQ9IjI5Mi4zNjEiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI5Mi4zNjIgMjkyLjM2MSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI4Ni45MzUgMTk3LjI4NyAxNTkuMDI4IDY5LjM4MWMtMy42MTMtMy42MTctNy44OTUtNS40MjQtMTIuODQ3LTUuNDI0cy05LjIzMyAxLjgwNy0xMi44NSA1LjQyNEw1LjQyNCAxOTcuMjg3QzEuODA3IDIwMC45MDQgMCAyMDUuMTg2IDAgMjEwLjEzNHMxLjgwNyA5LjIzMyA1LjQyNCAxMi44NDdjMy42MjEgMy42MTcgNy45MDIgNS40MjUgMTIuODUgNS40MjVoMjU1LjgxM2M0Ljk0OSAwIDkuMjMzLTEuODA4IDEyLjg0OC01LjQyNSAzLjYxMy0zLjYxMyA1LjQyNy03Ljg5OCA1LjQyNy0xMi44NDdzLTEuODE0LTkuMjMtNS40MjctMTIuODQ3eiIvPjwvc3ZnPg=="); - background-position-y: 35%; - background-size: 10px; -} -button.gridjs-sort-desc { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyOTIuMzYyIiBoZWlnaHQ9IjI5Mi4zNjIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI5Mi4zNjIgMjkyLjM2MiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZD0iTTI4Ni45MzUgNjkuMzc3Yy0zLjYxNC0zLjYxNy03Ljg5OC01LjQyNC0xMi44NDgtNS40MjRIMTguMjc0Yy00Ljk1MiAwLTkuMjMzIDEuODA3LTEyLjg1IDUuNDI0QzEuODA3IDcyLjk5OCAwIDc3LjI3OSAwIDgyLjIyOGMwIDQuOTQ4IDEuODA3IDkuMjI5IDUuNDI0IDEyLjg0N2wxMjcuOTA3IDEyNy45MDdjMy42MjEgMy42MTcgNy45MDIgNS40MjggMTIuODUgNS40MjhzOS4yMzMtMS44MTEgMTIuODQ3LTUuNDI4TDI4Ni45MzUgOTUuMDc0YzMuNjEzLTMuNjE3IDUuNDI3LTcuODk4IDUuNDI3LTEyLjg0NyAwLTQuOTQ4LTEuODE0LTkuMjI5LTUuNDI3LTEyLjg1eiIvPjwvc3ZnPg=="); - background-position-y: 65%; - background-size: 10px; -} -button.gridjs-sort:focus { - outline: none; -} -table.gridjs-table { - border-collapse: collapse; - display: table; - margin: 0; - max-width: 100%; - overflow: auto; - padding: 0; - table-layout: fixed; - text-align: left; - width: 100%; -} -.gridjs-tbody, -td.gridjs-td { - background-color: #fff; -} -td.gridjs-td { - border: 1px solid #e5e7eb; - box-sizing: content-box; - padding: 12px 24px; -} -td.gridjs-td:first-child { - border-left: none; -} -td.gridjs-td:last-child { - border-right: none; -} -td.gridjs-message { - text-align: center; -} -th.gridjs-th { - background-color: #f9fafb; - border: 1px solid #e5e7eb; - border-top: none; - box-sizing: border-box; - color: #6b7280; - outline: none; - padding: 14px 24px; - position: relative; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - vertical-align: middle; - white-space: nowrap; -} -th.gridjs-th .gridjs-th-content { - float: left; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} -th.gridjs-th-sort { - cursor: pointer; -} -th.gridjs-th-sort .gridjs-th-content { - width: calc(100% - 15px); -} -th.gridjs-th-sort:focus, -th.gridjs-th-sort:hover { - background-color: #e5e7eb; -} -th.gridjs-th-fixed { - box-shadow: 0 1px 0 0 #e5e7eb; - position: sticky; -} -@supports (-moz-appearance: none) { - th.gridjs-th-fixed { - box-shadow: 0 0 0 1px #e5e7eb; - } -} -th.gridjs-th:first-child { - border-left: none; -} -th.gridjs-th:last-child { - border-right: none; -} -.gridjs-tr { - border: none; -} -.gridjs-tr-selected td { - background-color: #ebf5ff; -} -.gridjs-tr:last-child td { - border-bottom: 0; -} -.gridjs *, -.gridjs :after, -.gridjs :before { - box-sizing: border-box; -} -.gridjs-wrapper { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - border-color: #e5e7eb; - border-radius: 8px 8px 0 0; - border-top-width: 1px; - box-shadow: - 0 1px 3px 0 rgba(0, 0, 0, 0.1), - 0 1px 2px 0 rgba(0, 0, 0, 0.26); - display: block; - overflow: auto; - position: relative; - width: 100%; - z-index: 1; -} -.gridjs-wrapper:nth-last-of-type(2) { - border-bottom-width: 1px; - border-radius: 8px; -} -.gridjs-search { - float: left; -} -.gridjs-search-input { - width: 250px; -} -.gridjs-loading-bar { - background-color: #fff; - opacity: 0.5; - z-index: 10; -} -.gridjs-loading-bar, -.gridjs-loading-bar:after { - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0; -} -.gridjs-loading-bar:after { - animation: shimmer 2s infinite; - background-image: linear-gradient( - 90deg, - hsla(0, 0%, 80%, 0), - hsla(0, 0%, 80%, 0.2) 20%, - hsla(0, 0%, 80%, 0.5) 60%, - hsla(0, 0%, 80%, 0) - ); - content: ""; - transform: translateX(-100%); -} -@keyframes shimmer { - to { - transform: translateX(100%); - } -} -.gridjs-td .gridjs-checkbox { - cursor: pointer; - display: block; - margin: auto; -} -.gridjs-resizable { - bottom: 0; - position: absolute; - right: 0; - top: 0; - width: 5px; -} -.gridjs-resizable:hover { - background-color: #9bc2f7; - cursor: ew-resize; -} diff --git a/sk/src/lib/pocketbase/generated-types.ts b/sk/src/lib/pocketbase/generated-types.ts index d597283..b3b6e23 100644 --- a/sk/src/lib/pocketbase/generated-types.ts +++ b/sk/src/lib/pocketbase/generated-types.ts @@ -56,6 +56,7 @@ export type ExpensesRecord = { }; export type UsersRecord = { + admin?: boolean; avatar?: string; name?: string; }; diff --git a/sk/src/lib/utils/file.utils.ts b/sk/src/lib/utils/file.utils.ts new file mode 100644 index 0000000..aa71ba9 --- /dev/null +++ b/sk/src/lib/utils/file.utils.ts @@ -0,0 +1,5 @@ +import { getBaseURL } from "./base"; + +export function getFileUrl(collectionId: string, recordId: string, filename: string): string { + return getBaseURL() + "/api/files/" + collectionId + "/" + recordId + "/" + filename; +} diff --git a/sk/src/routes/+layout.svelte b/sk/src/routes/+layout.svelte index d93fb70..d455490 100644 --- a/sk/src/routes/+layout.svelte +++ b/sk/src/routes/+layout.svelte @@ -1,6 +1,5 @@ @@ -72,6 +133,12 @@ Date Type Amount + Created + Picture + Company Credit Card + {#if client.authStore.model && client.authStore.model.admin} + User + {/if} Delete @@ -80,7 +147,12 @@ - + + + + {#if client.authStore.model && client.authStore.model.admin} + + {/if} @@ -92,6 +164,20 @@ {row.date} {row.type} {row.amount} + {row.created} + + {#if row.picture} + + + + {:else} + No picture + {/if} + + {row.company_credit_card ? "Yes" : "No"} + {#if client.authStore.model && client.authStore.model.admin} + {row.user} + {/if} @@ -119,10 +205,58 @@ +{#if client.authStore.model && client.authStore.model.admin} + +
+ + + +
+ +
+ + Select Month and Year + Select the month and year for the CSV export. + +
+
+ +
+
+ +
+
+ + + + + + +
+
+
+{/if} + diff --git a/sk/src/routes/app/analytics/+page.ts b/sk/src/routes/app/analytics/+page.ts new file mode 100644 index 0000000..e69de29