Compare commits
19 Commits
v1.2.4
...
c60907257e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c60907257e | ||
|
|
227c57ed41 | ||
|
|
c20ce31f04 | ||
|
|
55b6305a5e | ||
|
|
0026f68320 | ||
|
|
3f02afde85 | ||
|
|
2143ff1683 | ||
|
|
1fe2bd24b0 | ||
|
|
35fcd31a20 | ||
|
|
d2d7c83684 | ||
|
|
31929ea366 | ||
|
|
095d6411ba | ||
|
|
031b4d84ec | ||
|
|
1657123cc1 | ||
|
|
9a55d4c2f0 | ||
|
|
bf481da21e | ||
|
|
b0d225f7b8 | ||
|
|
5324787f23 | ||
|
|
bb626bf6b5 |
@@ -4,7 +4,7 @@ go 1.25.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.tecamino.com/paadi/access-handler v1.0.34
|
gitea.tecamino.com/paadi/access-handler v1.0.34
|
||||||
gitea.tecamino.com/paadi/memberDB v1.1.19
|
gitea.tecamino.com/paadi/memberDB v1.1.28
|
||||||
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1
|
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1
|
||||||
gitea.tecamino.com/paadi/tecamino-logger v0.2.1
|
gitea.tecamino.com/paadi/tecamino-logger v0.2.1
|
||||||
github.com/gin-contrib/cors v1.7.6
|
github.com/gin-contrib/cors v1.7.6
|
||||||
@@ -14,7 +14,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.tecamino.com/paadi/dbHandler v1.1.10 // indirect
|
gitea.tecamino.com/paadi/dbHandler v1.1.11 // indirect
|
||||||
github.com/bytedance/sonic v1.14.0 // indirect
|
github.com/bytedance/sonic v1.14.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
gitea.tecamino.com/paadi/access-handler v1.0.34 h1:6P65HiusSfvgv/ezOvxSahqyRJMK9UrxtGsz6loLoUk=
|
gitea.tecamino.com/paadi/access-handler v1.0.34 h1:6P65HiusSfvgv/ezOvxSahqyRJMK9UrxtGsz6loLoUk=
|
||||||
gitea.tecamino.com/paadi/access-handler v1.0.34/go.mod h1:HyMp1WvzmqLw8Ljt3r1qlF8fY+T5WFXr9Da/CTIM0H8=
|
gitea.tecamino.com/paadi/access-handler v1.0.34/go.mod h1:HyMp1WvzmqLw8Ljt3r1qlF8fY+T5WFXr9Da/CTIM0H8=
|
||||||
gitea.tecamino.com/paadi/dbHandler v1.1.10 h1:zZQbDTJ0bu6CIW90Zms8yYIzTLHtWPNhVKRxLUXEDuE=
|
gitea.tecamino.com/paadi/dbHandler v1.1.11 h1:hTpMWRr4dW7TkiBnEku0/3ggDC7/uP82U9paRKY/QEs=
|
||||||
gitea.tecamino.com/paadi/dbHandler v1.1.10/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw=
|
gitea.tecamino.com/paadi/dbHandler v1.1.11/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw=
|
||||||
gitea.tecamino.com/paadi/memberDB v1.1.19 h1:khbhtqS7rXTuOsWwxTO6rm13mIDjnBmJaTcJY4jmpQw=
|
gitea.tecamino.com/paadi/memberDB v1.1.28 h1:QSgPFIvzWS17bAIHp01nqUG5CQuE74AckrdYg6xZljw=
|
||||||
gitea.tecamino.com/paadi/memberDB v1.1.19/go.mod h1:VBsORoIIhh0/RM5AvmaAjMEM2/cNaIT2TqDL1VDcov4=
|
gitea.tecamino.com/paadi/memberDB v1.1.28/go.mod h1:uLoKel+EcuXUzxAY5ugfWh640TSomfTJR+g8Jfe8YKI=
|
||||||
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 h1:vAq7mwUxlxJuLzCQSDMrZCwo8ky5usWi9Qz+UP+WnkI=
|
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 h1:vAq7mwUxlxJuLzCQSDMrZCwo8ky5usWi9Qz+UP+WnkI=
|
||||||
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1/go.mod h1:+tmf1rjPaKEoNeUcr1vdtoFIFweNG3aUGevDAl3NMBk=
|
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1/go.mod h1:+tmf1rjPaKEoNeUcr1vdtoFIFweNG3aUGevDAl3NMBk=
|
||||||
gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE=
|
gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE=
|
||||||
|
|||||||
@@ -158,6 +158,8 @@ func main() {
|
|||||||
auth.POST("/events/delete/attendees", dbHandler.DeleteAttendee)
|
auth.POST("/events/delete/attendees", dbHandler.DeleteAttendee)
|
||||||
auth.POST("/events/delete", dbHandler.DeleteEvent)
|
auth.POST("/events/delete", dbHandler.DeleteEvent)
|
||||||
|
|
||||||
|
auth.POST("/report", dbHandler.GetReport)
|
||||||
|
|
||||||
auth.POST("/groups/add", dbHandler.NewGroup)
|
auth.POST("/groups/add", dbHandler.NewGroup)
|
||||||
auth.POST("/groups/edit", dbHandler.UpdateGroup)
|
auth.POST("/groups/edit", dbHandler.UpdateGroup)
|
||||||
auth.POST("/groups/delete", dbHandler.DeleteGroup)
|
auth.POST("/groups/delete", dbHandler.DeleteGroup)
|
||||||
|
|||||||
235
package-lock.json
generated
235
package-lock.json
generated
@@ -1,17 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor-community/sqlite": "^7.0.1",
|
"@capacitor-community/sqlite": "^7.0.1",
|
||||||
"@quasar/extras": "^1.17.0",
|
"@quasar/extras": "^1.17.0",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
|
"html2pdf.js": "^0.14.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
@@ -93,6 +94,15 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.28.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||||
|
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||||
@@ -1849,6 +1859,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pako": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.18",
|
"version": "6.9.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||||
@@ -1856,6 +1872,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/raf": {
|
||||||
|
"version": "3.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||||
|
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@types/range-parser": {
|
"node_modules/@types/range-parser": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||||
@@ -1886,6 +1909,13 @@
|
|||||||
"@types/send": "*"
|
"@types/send": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@types/zxcvbn": {
|
"node_modules/@types/zxcvbn": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.5.tgz",
|
||||||
@@ -2636,6 +2666,15 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -2964,6 +3003,26 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/canvg": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/raf": "^3.4.0",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"raf": "^3.4.1",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
|
"rgbcolor": "^1.0.1",
|
||||||
|
"stackblur-canvas": "^2.0.0",
|
||||||
|
"svg-pathdata": "^6.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -3320,6 +3379,18 @@
|
|||||||
"url": "https://github.com/sponsors/mesqueeb"
|
"url": "https://github.com/sponsors/mesqueeb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/core-js": {
|
||||||
|
"version": "3.48.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
|
||||||
|
"integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/core-js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/core-util-is": {
|
"node_modules/core-util-is": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
@@ -3368,6 +3439,15 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-line-break": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@@ -3505,6 +3585,15 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dot-case": {
|
"node_modules/dot-case": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||||
@@ -4281,6 +4370,23 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-png": {
|
||||||
|
"version": "6.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
|
||||||
|
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/pako": "^2.0.3",
|
||||||
|
"iobuffer": "^5.3.2",
|
||||||
|
"pako": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-png/node_modules/pako": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
@@ -4291,6 +4397,12 @@
|
|||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fflate": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||||
@@ -4734,6 +4846,30 @@
|
|||||||
"node": "^14.13.1 || >=16.0.0"
|
"node": "^14.13.1 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html2canvas": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-line-break": "^2.1.0",
|
||||||
|
"text-segmentation": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/html2pdf.js": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.14.0.tgz",
|
||||||
|
"integrity": "sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dompurify": "^3.3.1",
|
||||||
|
"html2canvas": "^1.0.0",
|
||||||
|
"jspdf": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/http-errors": {
|
"node_modules/http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
@@ -4865,6 +5001,12 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/iobuffer": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
@@ -5161,6 +5303,23 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jspdf": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-xd1d/XRkwqnsq6FP3zH1Q+Ejqn2ULIJeDZ+FTKpaabVpZREjsJKRJwuokTNgdqOU+fl55KgbvgZ1pRTSWCP2kQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.28.4",
|
||||||
|
"fast-png": "^6.2.0",
|
||||||
|
"fflate": "^0.8.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"canvg": "^3.0.11",
|
||||||
|
"core-js": "^3.6.0",
|
||||||
|
"dompurify": "^3.3.1",
|
||||||
|
"html2canvas": "^1.0.0-rc.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jszip": {
|
"node_modules/jszip": {
|
||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
@@ -5988,6 +6147,13 @@
|
|||||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/performance-now": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
@@ -6235,6 +6401,16 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/raf": {
|
||||||
|
"version": "3.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||||
|
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"performance-now": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@@ -6349,6 +6525,13 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/relateurl": {
|
"node_modules/relateurl": {
|
||||||
"version": "0.2.7",
|
"version": "0.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||||
@@ -6417,6 +6600,16 @@
|
|||||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/rgbcolor": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||||
|
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.40.1",
|
"version": "4.40.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz",
|
||||||
@@ -7365,6 +7558,16 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stackblur-canvas": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
@@ -7495,6 +7698,16 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg-pathdata": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sync-child-process": {
|
"node_modules/sync-child-process": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
|
||||||
@@ -7583,6 +7796,15 @@
|
|||||||
"b4a": "^1.6.4"
|
"b4a": "^1.6.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/text-segmentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tiny-invariant": {
|
"node_modules/tiny-invariant": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
@@ -7873,6 +8095,15 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/utrie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/varint": {
|
"node_modules/varint": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lightcontrol",
|
"name": "lightcontrol",
|
||||||
"version": "1.2.4",
|
"version": "1.3.0",
|
||||||
"description": "A Tecamino App",
|
"description": "A Tecamino App",
|
||||||
"productName": "Attendence Records",
|
"productName": "Attendence Records",
|
||||||
"author": "A. Zuercher",
|
"author": "A. Zuercher",
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
"@capacitor-community/sqlite": "^7.0.1",
|
"@capacitor-community/sqlite": "^7.0.1",
|
||||||
"@quasar/extras": "^1.17.0",
|
"@quasar/extras": "^1.17.0",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
|
"html2pdf.js": "^0.14.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export default defineConfig((/* ctx */) => {
|
|||||||
//directives: [],
|
//directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: ['Notify', 'Dialog'],
|
plugins: ['Cookies', 'Notify', 'Dialog'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ filterByColumn: Spaltenfilter
|
|||||||
filterByColumnValue: Spaltenwerte
|
filterByColumnValue: Spaltenwerte
|
||||||
saveAsDefault: Aus Standard spichere
|
saveAsDefault: Aus Standard spichere
|
||||||
day: Tag
|
day: Tag
|
||||||
MondayShort: Mäntig
|
MondayShort: Mo
|
||||||
Monday: Mo
|
Monday: Mäntig
|
||||||
Tuesday: Zistig
|
Tuesday: Zistig
|
||||||
TuesdayShort: Di
|
TuesdayShort: Di
|
||||||
Wednesday: Mittwuch
|
Wednesday: Mittwuch
|
||||||
@@ -156,3 +156,17 @@ numberOfEvents: Anzau Veranstautige
|
|||||||
numberOfResponsibles: Anzau Veratwortläche
|
numberOfResponsibles: Anzau Veratwortläche
|
||||||
numberOfGroups: Anzau Gruppe
|
numberOfGroups: Anzau Gruppe
|
||||||
selectDates: Datumuswauh
|
selectDates: Datumuswauh
|
||||||
|
apply: Awende
|
||||||
|
minimal: Minimal
|
||||||
|
maximal: Maximal
|
||||||
|
average: Durchschnitt
|
||||||
|
filterEventName: Verastautig filtere
|
||||||
|
hintFilterEventName: "*'IIgabe'* * oder % als filler vorher oder nächer"
|
||||||
|
total: Gesamt
|
||||||
|
exportPdf: PDF exportiere
|
||||||
|
print: Drucke
|
||||||
|
today: Hüt
|
||||||
|
week: Wuche
|
||||||
|
month: Monat
|
||||||
|
year: Jahr
|
||||||
|
appName: Applikationsname
|
||||||
|
|||||||
@@ -156,3 +156,17 @@ numberOfEvents: Anzahl Veranstaltungen
|
|||||||
numberOfResponsibles: Anzahl Verantwortliche
|
numberOfResponsibles: Anzahl Verantwortliche
|
||||||
numberOfGroups: Anzahl Gruppe
|
numberOfGroups: Anzahl Gruppe
|
||||||
selectDates: Datumauswahl
|
selectDates: Datumauswahl
|
||||||
|
apply: Anwenden
|
||||||
|
minimal: Minimal
|
||||||
|
maximal: Maximal
|
||||||
|
average: Durchschnitt
|
||||||
|
filterEventName: Veranstaltung filtern
|
||||||
|
hintFilterEventName: "*'Eingabe'* * oder % als Filler vorher oder nachher"
|
||||||
|
total: Gesamt
|
||||||
|
exportPdf: PDF exportieren
|
||||||
|
print: Drucken
|
||||||
|
today: Heute
|
||||||
|
week: Woche
|
||||||
|
month: Monat
|
||||||
|
year: Jahr
|
||||||
|
appName: Applikationsname
|
||||||
|
|||||||
@@ -156,3 +156,17 @@ numberOfEvents: Amount of Events
|
|||||||
numberOfResponsibles: Amount of Responsibles
|
numberOfResponsibles: Amount of Responsibles
|
||||||
numberOfGroups: Amount of Groups
|
numberOfGroups: Amount of Groups
|
||||||
selectDates: Dateselection
|
selectDates: Dateselection
|
||||||
|
apply: Apply
|
||||||
|
minimal: Minimal
|
||||||
|
maximal: Maximal
|
||||||
|
average: Average
|
||||||
|
filterEventName: filter Events
|
||||||
|
hintFilterEventName: "*'Input'* * or % as filler before oder after"
|
||||||
|
total: Total
|
||||||
|
exportPdf: export PDF
|
||||||
|
print: Print
|
||||||
|
today: Today
|
||||||
|
week: Week
|
||||||
|
month: Month
|
||||||
|
year: Year
|
||||||
|
appName: Applicationname
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ FridayShort: Vi
|
|||||||
Saturday: Sábado
|
Saturday: Sábado
|
||||||
SaturdayShort: Sá
|
SaturdayShort: Sá
|
||||||
Sunday: Domingo
|
Sunday: Domingo
|
||||||
Sunday: Do
|
SundayShort: Do
|
||||||
currentPassword: Contraseña actual
|
currentPassword: Contraseña actual
|
||||||
addFirstUser: Añadir primer usuario administrador
|
addFirstUser: Añadir primer usuario administrador
|
||||||
report: Informe
|
report: Informe
|
||||||
@@ -156,3 +156,17 @@ numberOfEvents: Cantidad de eventos
|
|||||||
numberOfResponsibles: Número de responsables
|
numberOfResponsibles: Número de responsables
|
||||||
numberOfGroups: Número de grupos
|
numberOfGroups: Número de grupos
|
||||||
selectDates: Selección de fecha
|
selectDates: Selección de fecha
|
||||||
|
apply: Aplicar
|
||||||
|
minimal: Mínimo
|
||||||
|
maximal: Máximo
|
||||||
|
average: Promedio
|
||||||
|
filterEventName: filtrar eventos
|
||||||
|
hintFilterEventName: "*''Entrada'* * o % como relleno antes o después"
|
||||||
|
total: Total
|
||||||
|
exportPdf: exportar PDF
|
||||||
|
print: Imprimir
|
||||||
|
today: Hoy
|
||||||
|
week: Semana
|
||||||
|
month: Mes
|
||||||
|
year: Año
|
||||||
|
appName: Nombre de la aplicación
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="q-gutter-sm">
|
<div
|
||||||
|
class="q-ma-sm q-gutter-sm"
|
||||||
|
:style="{ height: props.height + 'px', width: props.width + 'px' }"
|
||||||
|
>
|
||||||
<h6 class="text-center text-bold q-ma-md text-primary">{{ props.title }}</h6>
|
<h6 class="text-center text-bold q-ma-md text-primary">{{ props.title }}</h6>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-checkbox
|
<q-checkbox
|
||||||
@@ -11,33 +14,54 @@
|
|||||||
:val="opt.value"
|
:val="opt.value"
|
||||||
:label="opt.label"
|
:label="opt.label"
|
||||||
/>
|
/>
|
||||||
|
<q-tabs
|
||||||
|
v-model="activeTab"
|
||||||
|
dense
|
||||||
|
class="text-primary"
|
||||||
|
active-color="primary"
|
||||||
|
indicator-color="primary"
|
||||||
|
align="justify"
|
||||||
|
narrow-indicator
|
||||||
|
@update:model-value="onTabChange"
|
||||||
|
>
|
||||||
|
<q-tab no-caps name="today" :label="$t('today')" />
|
||||||
|
<q-tab no-caps name="week" :label="$t('week')" />
|
||||||
|
<q-tab no-caps name="month" :label="$t('month')" />
|
||||||
|
<q-tab no-caps name="year" :label="$t('year')" />
|
||||||
|
</q-tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-date v-model="dateRange" range flat />
|
<q-date v-model="dateRange" range flat />
|
||||||
<div>
|
|
||||||
<q-badge color="secondary"> Total dates selected: {{ filteredDates.length }} </q-badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, onMounted, watch, type PropType } from 'vue';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { i18n } from 'src/boot/lang';
|
import { i18n } from 'src/boot/lang';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
|
height: { type: Number, default: 400 },
|
||||||
|
width: { type: Number, default: 300 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const weekdays = defineModel('weekdays', {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => [0, 3],
|
||||||
});
|
});
|
||||||
|
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
|
|
||||||
// Initial range (format: YYYY/MM/DD)
|
const activeTab = ref('');
|
||||||
const dateRange = ref();
|
// Initial range (format: YYYY-MM-DD)
|
||||||
const selectedWeekdays = ref([0, 3]); // Default to weekdays
|
const dateRange = ref<string | { to: string; from: string }>('');
|
||||||
|
const selectedWeekdays = ref(weekdays); // Default to weekdays
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:dates']);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
dateRange.value = date.formatDate(startDate, 'YYYY/MM/DD');
|
dateRange.value = date.formatDate(startDate, 'YYYY-MM-DD');
|
||||||
});
|
});
|
||||||
|
|
||||||
const weekdayOptions = [
|
const weekdayOptions = [
|
||||||
@@ -50,14 +74,23 @@ const weekdayOptions = [
|
|||||||
{ label: i18n.global.t('SundayShort'), value: 0 },
|
{ label: i18n.global.t('SundayShort'), value: 0 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const onTabChange = (val: 'today' | 'week' | 'month' | 'year') => {
|
||||||
|
if (val) setRange(val);
|
||||||
|
// Optional: Reset tab to empty so user can click the same tab again later
|
||||||
|
setTimeout(() => {
|
||||||
|
activeTab.value = '';
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
// The Logic: Calculate all specific dates within the range that match weekdays
|
// The Logic: Calculate all specific dates within the range that match weekdays
|
||||||
const filteredDates = computed(() => {
|
watch(dateRange, () => {
|
||||||
if (!dateRange.value) {
|
if (!dateRange.value) {
|
||||||
return [];
|
return [];
|
||||||
} else if (typeof dateRange.value === 'string') {
|
} else if (typeof dateRange.value === 'string') {
|
||||||
const current = new Date(dateRange.value);
|
const current = new Date(dateRange.value);
|
||||||
if (current !== undefined && selectedWeekdays.value.includes(current.getDay())) {
|
if (current !== undefined && selectedWeekdays.value.includes(current.getDay())) {
|
||||||
return [date.formatDate(current, 'YYYY/MM/DD')];
|
emit('update:dates', [date.formatDate(current, 'YYYY-MM-DD')]);
|
||||||
|
return [date.formatDate(current, 'YYYY-MM-DD')];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -69,10 +102,89 @@ const filteredDates = computed(() => {
|
|||||||
|
|
||||||
while (current <= end) {
|
while (current <= end) {
|
||||||
if (selectedWeekdays.value.includes(current.getDay())) {
|
if (selectedWeekdays.value.includes(current.getDay())) {
|
||||||
result.push(date.formatDate(current, 'YYYY/MM/DD'));
|
result.push(date.formatDate(current, 'YYYY-MM-DD'));
|
||||||
}
|
}
|
||||||
current = date.addToDate(current, { days: 1 });
|
current = date.addToDate(current, { days: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit('update:dates', result);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic for 1 Week / 1 Month / 1 Year
|
||||||
|
*/
|
||||||
|
const setRange = (type: 'today' | 'week' | 'month' | 'year') => {
|
||||||
|
const anchor =
|
||||||
|
typeof dateRange.value === 'string'
|
||||||
|
? new Date(dateRange.value)
|
||||||
|
: new Date(dateRange.value.from);
|
||||||
|
|
||||||
|
let from: Date;
|
||||||
|
let to: Date;
|
||||||
|
|
||||||
|
if (type === 'today') {
|
||||||
|
const now = new Date();
|
||||||
|
dateRange.value = date.formatDate(date.startOfDate(now, 'day'), 'YYYY-MM-DD');
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (type === 'week') {
|
||||||
|
// getDay() returns 0 for Sunday.
|
||||||
|
// We calculate how many days to subtract to get to Monday (1).
|
||||||
|
const day = anchor.getDay();
|
||||||
|
const diffToMonday = day === 0 ? -6 : 1 - day;
|
||||||
|
|
||||||
|
from = date.addToDate(anchor, { days: diffToMonday });
|
||||||
|
from = date.startOfDate(from, 'day'); // Reset time to 00:00
|
||||||
|
|
||||||
|
to = date.addToDate(from, { days: 6 });
|
||||||
|
to = date.endOfDate(to, 'day'); // Set time to 23:59
|
||||||
|
} else if (type === 'month') {
|
||||||
|
// 'month' is a valid unit for startOfDate
|
||||||
|
from = date.startOfDate(anchor, 'month');
|
||||||
|
to = date.endOfDate(anchor, 'month');
|
||||||
|
} else {
|
||||||
|
// 'year' is a valid unit for startOfDate
|
||||||
|
from = date.startOfDate(anchor, 'year');
|
||||||
|
to = date.endOfDate(anchor, 'year');
|
||||||
|
}
|
||||||
|
|
||||||
|
dateRange.value = {
|
||||||
|
from: date.formatDate(from, 'YYYY/MM/DD'),
|
||||||
|
to: date.formatDate(to, 'YYYY/MM/DD'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[dateRange, selectedWeekdays],
|
||||||
|
() => {
|
||||||
|
if (!dateRange.value) {
|
||||||
|
emit('update:dates', []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start: Date, end: Date;
|
||||||
|
|
||||||
|
if (typeof dateRange.value === 'string') {
|
||||||
|
start = new Date(dateRange.value);
|
||||||
|
end = new Date(dateRange.value);
|
||||||
|
} else {
|
||||||
|
start = new Date(dateRange.value.from);
|
||||||
|
end = new Date(dateRange.value.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
let current = start;
|
||||||
|
|
||||||
|
while (current <= end) {
|
||||||
|
if (selectedWeekdays.value.includes(current.getDay())) {
|
||||||
|
result.push(date.formatDate(current, 'YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
current = date.addToDate(current, { days: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update:dates', result);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -69,7 +69,6 @@
|
|||||||
filled
|
filled
|
||||||
emit-value
|
emit-value
|
||||||
map-options
|
map-options
|
||||||
option-value="name"
|
|
||||||
option-label="name"
|
option-label="name"
|
||||||
v-model="localMember.group"
|
v-model="localMember.group"
|
||||||
></q-select>
|
></q-select>
|
||||||
@@ -107,7 +106,7 @@
|
|||||||
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
||||||
import { type PropType, ref } from 'vue';
|
import { type PropType, ref } from 'vue';
|
||||||
import { appApi } from 'src/boot/axios';
|
import { appApi } from 'src/boot/axios';
|
||||||
import type { Member } from 'src/vueLib/models/member';
|
import type { Member, Members } from 'src/vueLib/models/member';
|
||||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||||
import { i18n } from 'src/boot/lang';
|
import { i18n } from 'src/boot/lang';
|
||||||
import type { Responsibles } from 'src/vueLib/models/responsible';
|
import type { Responsibles } from 'src/vueLib/models/responsible';
|
||||||
@@ -127,7 +126,7 @@ const props = defineProps({
|
|||||||
type: Object as PropType<Responsibles>,
|
type: Object as PropType<Responsibles>,
|
||||||
},
|
},
|
||||||
group: {
|
group: {
|
||||||
type: Array,
|
type: Object as PropType<Members>,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
53
src/components/ReportStat.vue
Normal file
53
src/components/ReportStat.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<q-card class="col-auto">
|
||||||
|
<div class="row q-col-gutter-xs">
|
||||||
|
<div v-for="opt in props.amounts" :key="opt.name">
|
||||||
|
<q-card class="q-ma-xs" flat bordered>
|
||||||
|
<q-item dense>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="text-bold text-primary text-center">
|
||||||
|
{{ $t(opt.name) }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-section>
|
||||||
|
<div class="column justify-between items-center q-mb-xs">
|
||||||
|
<span class="text-bold text-grey-7 text-caption">{{ $t('events') }}</span>
|
||||||
|
<q-badge color="black" outline class="text-bold">{{ opt.events }}</q-badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column justify-between items-center q-mb-xs">
|
||||||
|
<span class="text-bold text-grey-7 text-caption">{{ $t('minimal') }}</span>
|
||||||
|
<q-badge color="secondary" outline class="text-bold">{{ opt.minimal }}</q-badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column justify-between items-center q-mb-xs">
|
||||||
|
<span class="text-bold text-grey-7 text-caption">{{ $t('average') }}</span>
|
||||||
|
<q-badge color="secondary" outline class="text-bold">{{ opt.average }}</q-badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column justify-between items-center q-mb-xs">
|
||||||
|
<span class="text-bold text-grey-7 text-caption">{{ $t('maximal') }}</span>
|
||||||
|
<q-badge color="primary" outline class="text-bold">{{ opt.maximal }}</q-badge>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Amount } from 'src/vueLib/models/report';
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
amounts: {
|
||||||
|
type: Array as PropType<Amount[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -3,11 +3,13 @@
|
|||||||
<q-header elevated>
|
<q-header elevated>
|
||||||
<q-toolbar>
|
<q-toolbar>
|
||||||
<q-img
|
<q-img
|
||||||
|
v-if="localLogo !== undefined && localLogo !== ''"
|
||||||
:src="localLogo"
|
:src="localLogo"
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
style="width: 40px; height: 40px; background-color: var(--q-primary)"
|
style="width: 40px; height: 40px; background-color: var(--q-primary)"
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" />
|
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" />
|
||||||
|
|
||||||
<q-toolbar-title class="text-primary-text"> {{ $t(appName) }} </q-toolbar-title>
|
<q-toolbar-title class="text-primary-text"> {{ $t(appName) }} </q-toolbar-title>
|
||||||
@@ -63,10 +65,7 @@
|
|||||||
>
|
>
|
||||||
<q-item-section>{{ $t('groups') }}</q-item-section>
|
<q-item-section>{{ $t('groups') }}</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
<q-item v-if="!autorized" to="/login" exact clickable v-ripple @click="closeDrawer">
|
<q-item
|
||||||
<q-item-section>{{ $t('login') }}</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<!-- <q-item
|
|
||||||
v-if="autorized || user.isPermittedTo('members', 'read')"
|
v-if="autorized || user.isPermittedTo('members', 'read')"
|
||||||
to="/report"
|
to="/report"
|
||||||
exact
|
exact
|
||||||
@@ -75,7 +74,7 @@
|
|||||||
@click="closeDrawer"
|
@click="closeDrawer"
|
||||||
>
|
>
|
||||||
<q-item-section> {{ $t('report') }}</q-item-section>
|
<q-item-section> {{ $t('report') }}</q-item-section>
|
||||||
</q-item> -->
|
</q-item>
|
||||||
<q-item v-if="autorized" to="/stats" exact clickable v-ripple @click="closeDrawer">
|
<q-item v-if="autorized" to="/stats" exact clickable v-ripple @click="closeDrawer">
|
||||||
<q-item-section> {{ $t('stats') }}</q-item-section>
|
<q-item-section> {{ $t('stats') }}</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Dark } from 'quasar';
|
import { Dark } from 'quasar';
|
||||||
import { appName, databaseName, type Settings } from 'src/vueLib/models/settings';
|
import { appName, databaseName, type Settings } from 'src/vueLib/models/settings';
|
||||||
import { updateOrAddObject } from 'src/vueLib/utils/utils';
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
export function setLocalSettings(settings: Settings) {
|
export function setLocalSettings(settings: Settings) {
|
||||||
@@ -24,8 +23,13 @@ export function getLocalSettings(): Settings {
|
|||||||
db = databaseName.value;
|
db = databaseName.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let iconName = localStorage.getItem('icon');
|
||||||
|
if (iconName === undefined || iconName === 'undefined') {
|
||||||
|
iconName = '';
|
||||||
|
}
|
||||||
|
|
||||||
return <Settings>{
|
return <Settings>{
|
||||||
icon: localStorage.getItem('icon'),
|
icon: iconName,
|
||||||
appName: name,
|
appName: name,
|
||||||
databaseName: db,
|
databaseName: db,
|
||||||
primaryColor: localStorage.getItem('primaryColor'),
|
primaryColor: localStorage.getItem('primaryColor'),
|
||||||
@@ -74,30 +78,19 @@ export function getLocalLanguage(): string | null {
|
|||||||
|
|
||||||
type pageDefault = {
|
type pageDefault = {
|
||||||
page: string;
|
page: string;
|
||||||
filteredColumn: string;
|
data: unknown;
|
||||||
filteredValue: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type pageDefaults = pageDefault[];
|
type pageDefaults = pageDefault[];
|
||||||
|
|
||||||
const pageDefaults = ref<pageDefaults>([]);
|
const pageDefaults = ref<pageDefaults>([]);
|
||||||
|
|
||||||
export function setLocalPageDefaults(
|
export function setLocalPageDefaults(page: string, data: unknown) {
|
||||||
page: string,
|
localStorage.setItem(page + 'Defaults', JSON.stringify(data));
|
||||||
filteredColumn?: string,
|
|
||||||
filteredValue?: string[],
|
|
||||||
) {
|
|
||||||
updateOrAddObject(
|
|
||||||
pageDefaults.value,
|
|
||||||
{ page: page, filteredColumn: filteredColumn, filteredValue: filteredValue },
|
|
||||||
'page',
|
|
||||||
);
|
|
||||||
localStorage.setItem('pageDefaults', JSON.stringify(pageDefaults.value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalPageDefaults(page: string): pageDefault | null {
|
export function getLocalPageDefaults(page: string): unknown {
|
||||||
const defaults = localStorage.getItem('pageDefaults');
|
const defaults = localStorage.getItem(page + 'Defaults');
|
||||||
if (!defaults) return null;
|
if (!defaults) return null;
|
||||||
pageDefaults.value = JSON.parse(defaults);
|
return JSON.parse(defaults);
|
||||||
return pageDefaults.value.find((e) => e.page === page) || null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,364 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="rows">
|
<q-inner-loading
|
||||||
|
:showing="loading"
|
||||||
|
label="Please wait..."
|
||||||
|
label-class="text-teal"
|
||||||
|
label-style="font-size: 1.1em"
|
||||||
|
/>
|
||||||
|
<div class="colums">
|
||||||
<div class="row justify-end">
|
<div class="row justify-end">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<DateDaySelect title="hjgjh" />
|
<q-btn-dropdown
|
||||||
|
no-caps
|
||||||
|
ref="dropdownRef"
|
||||||
|
class="q-ma-sm"
|
||||||
|
color="primary"
|
||||||
|
:label="$t('selectDates')"
|
||||||
|
@show="loadSettings"
|
||||||
|
>
|
||||||
|
<DateDaySelect @update:dates="updateReport" v-model:weekdays="weekdays" />
|
||||||
|
<div class="column justify-end q-pa-md">
|
||||||
|
<div class="row q-ma-md">
|
||||||
|
<q-input
|
||||||
|
class="col-7"
|
||||||
|
label-color="primary"
|
||||||
|
:label="$t('filterEventName')"
|
||||||
|
:hint="$t('hintFilterEventName')"
|
||||||
|
type="text"
|
||||||
|
v-model:model-value="filter"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
<div class="row q-ma-md">
|
||||||
|
<q-select
|
||||||
|
class="col-7"
|
||||||
|
:label="$t('filterByColumnValue')"
|
||||||
|
dense
|
||||||
|
v-model="group"
|
||||||
|
:options="groups"
|
||||||
|
option-label="name"
|
||||||
|
option-value="id"
|
||||||
|
multiple
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-end">
|
||||||
|
<q-btn dense class="q-ma-md" color="primary" no-caps @click="applyDateChoice">{{
|
||||||
|
$t('apply')
|
||||||
|
}}</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-btn-dropdown>
|
||||||
|
<div class="column q-ma-sm" v-if="amounts.length">
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
no-caps
|
||||||
|
class="q-ma-sm"
|
||||||
|
color="grey-9"
|
||||||
|
icon="print"
|
||||||
|
:label="$t('print')"
|
||||||
|
@click="printReport"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
no-caps
|
||||||
|
color="secondary"
|
||||||
|
icon="picture_as_pdf"
|
||||||
|
:label="$t('exportPdf')"
|
||||||
|
@click="downloadPDF"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="report-content" ref="reportExportRef">
|
||||||
|
<div class="row justify-center q-ma-xs">
|
||||||
|
<h3 class="col-12 text-center text-primary text-bold">{{ $t('report') }}</h3>
|
||||||
|
<ReportStat :amounts="amounts" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-center">
|
||||||
|
<div
|
||||||
|
v-if="attendees !== undefined"
|
||||||
|
:class="
|
||||||
|
nonAttendees !== undefined
|
||||||
|
? 'col-12 col-sm-5 col-md-5 q-pa-md'
|
||||||
|
: 'col-12 col-md-8 col-lg-5'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-table
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:no-data-label="$t('noDataAvailable')"
|
||||||
|
:loading-label="$t('loading')"
|
||||||
|
:rows-per-page-label="$t('recordsPerPage')"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
:title="$t('attendees')"
|
||||||
|
title-class="text-bold text-primary"
|
||||||
|
:rows="attendees"
|
||||||
|
:columns="columns"
|
||||||
|
>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="nonAttendees !== undefined"
|
||||||
|
:class="
|
||||||
|
attendees !== undefined ? 'col-12 col-sm-5 col-md-5 q-pa-md' : 'col-12 col-md-8 col-lg-5'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-table
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:title="$t('noneAttendees')"
|
||||||
|
:no-data-label="$t('noDataAvailable')"
|
||||||
|
:loading-label="$t('loading')"
|
||||||
|
:rows-per-page-label="$t('recordsPerPage')"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
title-class="text-bold text-primary"
|
||||||
|
:rows="nonAttendees"
|
||||||
|
:columns="columns"
|
||||||
|
>
|
||||||
|
</q-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-btn @click="openDateSelect">Hallo</q-btn>
|
|
||||||
<DialogFrame :header-title="$t('selectDates')" :width="350" :height="500" ref="dateSelect">
|
|
||||||
<DateDaySelect :title="$t('selectDates')" />
|
|
||||||
<q-btn color="primary" no-caps>{{ $t('apply') }}</q-btn>
|
|
||||||
</DialogFrame>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { appApi } from 'src/boot/axios';
|
import { appApi } from 'src/boot/axios';
|
||||||
import DateDaySelect from 'src/components/DateDaySelect.vue';
|
import DateDaySelect from 'src/components/DateDaySelect.vue';
|
||||||
import { onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||||
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
|
import { i18n } from 'src/boot/lang';
|
||||||
|
import { appName, databaseName } from 'src/vueLib/models/settings';
|
||||||
|
import type { Amount } from 'src/vueLib/models/report';
|
||||||
|
import ReportStat from 'src/components/ReportStat.vue';
|
||||||
|
import type { Group, Groups } from 'src/vueLib/models/group';
|
||||||
|
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
|
import html2pdf from 'html2pdf.js';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pageDefaults';
|
||||||
|
|
||||||
const dateSelect = ref();
|
const filter = ref<string>('');
|
||||||
|
const group = ref<Group[]>([]);
|
||||||
|
const groups = ref<Groups>([]);
|
||||||
|
const allDates = ref<string[]>([]);
|
||||||
|
const attendees = ref();
|
||||||
|
const nonAttendees = ref();
|
||||||
const { NotifyResponse } = useNotify();
|
const { NotifyResponse } = useNotify();
|
||||||
|
const dropdownRef = ref();
|
||||||
|
const loading = ref(false);
|
||||||
|
const amounts = ref<Amount[]>([]);
|
||||||
|
const reportExportRef = ref<HTMLElement | null>(null);
|
||||||
|
const weekdays = ref<number[]>([0, 3]);
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'firstName',
|
||||||
|
align: 'left' as const,
|
||||||
|
label: i18n.global.t('prename'),
|
||||||
|
field: 'firstName',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastName',
|
||||||
|
align: 'left' as const,
|
||||||
|
label: i18n.global.t('lastName'),
|
||||||
|
field: 'lastName',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
loading.value = true;
|
||||||
appApi
|
appApi
|
||||||
.get('events')
|
.post('database/open', { dbPath: databaseName.value })
|
||||||
.then((resp) => console.log(1, resp))
|
.catch((err) => NotifyResponse(err, 'error'))
|
||||||
.catch((err) => NotifyResponse(err, 'error'));
|
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
appApi
|
||||||
|
.get('/groups')
|
||||||
|
.then((resp) => (groups.value = resp.data))
|
||||||
|
.catch((err) => NotifyResponse(err, 'error'))
|
||||||
|
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function openDateSelect() {
|
function loadSettings() {
|
||||||
dateSelect.value?.open();
|
const settings = getLocalPageDefaults('report') as PageDefault;
|
||||||
|
if (!settings) return;
|
||||||
|
if (settings.groups) {
|
||||||
|
group.value = settings.groups;
|
||||||
|
}
|
||||||
|
if (settings.weekdays) {
|
||||||
|
weekdays.value = settings.weekdays;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function applyDateChoice() {
|
||||||
|
amounts.value = [];
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
dropdownRef.value.hide();
|
||||||
|
|
||||||
|
const payload: { name: null | string[]; date: string[]; groupIds: number[] | null } = {
|
||||||
|
name: null,
|
||||||
|
date: allDates.value,
|
||||||
|
groupIds: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filter.value) {
|
||||||
|
//name is a array of string and search works as example Test*, Ge*, *st
|
||||||
|
payload.name = [filter.value];
|
||||||
|
}
|
||||||
|
if (group.value) {
|
||||||
|
// group has to be array of id numbers
|
||||||
|
payload.groupIds = group.value.map((g) => g.id);
|
||||||
|
}
|
||||||
|
payload.date = allDates.value;
|
||||||
|
|
||||||
|
setLocalPageDefaults('report', <PageDefault>{ groups: group.value, weekdays: weekdays.value });
|
||||||
|
|
||||||
|
appApi
|
||||||
|
.post('report', payload)
|
||||||
|
.then((resp) => {
|
||||||
|
attendees.value = [];
|
||||||
|
nonAttendees.value = [];
|
||||||
|
if (!resp.data) return;
|
||||||
|
if (resp.data.data === undefined) return;
|
||||||
|
const data = resp.data.data;
|
||||||
|
if (data.data === undefined) return;
|
||||||
|
|
||||||
|
if (data.attendees) {
|
||||||
|
attendees.value = data.attendees;
|
||||||
|
}
|
||||||
|
if (data.attendees) {
|
||||||
|
nonAttendees.value = data.nonAttendees;
|
||||||
|
}
|
||||||
|
|
||||||
|
const days = [
|
||||||
|
'Monday',
|
||||||
|
'Tuesday',
|
||||||
|
'Wednesday',
|
||||||
|
'Thursday',
|
||||||
|
'Friday',
|
||||||
|
'Saturday',
|
||||||
|
'Sunday',
|
||||||
|
'total',
|
||||||
|
];
|
||||||
|
|
||||||
|
amounts.value = days
|
||||||
|
.filter((day) => data.data[day]) // Only include days that exist in the response
|
||||||
|
.map((day) => ({
|
||||||
|
...data.data[day],
|
||||||
|
name: day, // Dynamically translate the name
|
||||||
|
}));
|
||||||
|
if (amounts.value.length == 2) {
|
||||||
|
amounts.value.splice(1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => NotifyResponse(err, 'error'))
|
||||||
|
.finally(() => (loading.value = false));
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateReport(dates: string[]) {
|
||||||
|
allDates.value = dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printReport() {
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadPDF() {
|
||||||
|
const element = reportExportRef.value;
|
||||||
|
if (!element) return;
|
||||||
|
// Generate date string (YYYY-MM-DD)
|
||||||
|
const today = new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// Optionally, add time for more precision (HH-mm)
|
||||||
|
const time = new Date()
|
||||||
|
.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
|
||||||
|
.replace(':', '-');
|
||||||
|
const options = {
|
||||||
|
margin: [10, 10, 10, 10] as [number, number, number, number],
|
||||||
|
filename: appName.value + `${today}_${time}.pdf`,
|
||||||
|
image: {
|
||||||
|
type: 'jpeg' as const,
|
||||||
|
quality: 1.0, // Set quality to 100%
|
||||||
|
},
|
||||||
|
html2canvas: {
|
||||||
|
scale: 4, // Increase this (2 is standard, 4 is crisp/retina)
|
||||||
|
useCORS: true,
|
||||||
|
letterRendering: true, // Improves text spacing
|
||||||
|
dpi: 300, // Standard print resolution
|
||||||
|
},
|
||||||
|
jsPDF: {
|
||||||
|
unit: 'mm' as const,
|
||||||
|
format: 'a4' as const,
|
||||||
|
orientation: 'portrait' as const,
|
||||||
|
compress: true, // Keeps file size manageable despite high scale
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await html2pdf()
|
||||||
|
.set(options)
|
||||||
|
.from(element)
|
||||||
|
.save()
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('PDF Generation failed:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
/* 1. Hide the URL, Date, and Page Title */
|
||||||
|
@page {
|
||||||
|
margin: 0; /* This is what removes the URL and headers/footers */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding: 1.5cm; /* Add padding here so the content isn't at the very edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Hide UI elements */
|
||||||
|
.q-btn,
|
||||||
|
.q-btn-dropdown,
|
||||||
|
.q-header,
|
||||||
|
.q-drawer,
|
||||||
|
.q-footer,
|
||||||
|
.q-notifications {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Ensure the layout uses full width */
|
||||||
|
.q-page-container {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove shadows for cleaner printing */
|
||||||
|
.q-card,
|
||||||
|
.q-table__card {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: 1px solid #ddd !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* This ensures the PDF version has a white background and visible text */
|
||||||
|
#report-content {
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force tables to expand to full width in the PDF */
|
||||||
|
#report-content .q-table__container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If you want to force a page break before the tables */
|
||||||
|
.pdf-page-break {
|
||||||
|
page-break-before: always;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -7,25 +7,17 @@
|
|||||||
<p class="text-bold text-h6 text-primary q-pa-md">{{ $t('general') }}</p>
|
<p class="text-bold text-h6 text-primary q-pa-md">{{ $t('general') }}</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-input
|
<q-input
|
||||||
|
dense
|
||||||
:readonly="!user.isPermittedTo('settings', 'write')"
|
:readonly="!user.isPermittedTo('settings', 'write')"
|
||||||
:class="[
|
:class="[colorGroup ? ' col-md-4' : 'col-md-12', 'q-pa-md']"
|
||||||
colorGroup ? 'col-md-4' : 'col-md-3',
|
|
||||||
colorGroup ? 'col-md-6' : 'col-md-6',
|
|
||||||
colorGroup ? 'col-md-4' : 'col-md-12',
|
|
||||||
'q-pa-md',
|
|
||||||
]"
|
|
||||||
filled
|
filled
|
||||||
:label="$t('appName')"
|
:label="$t('appName')"
|
||||||
v-model="settings.appName"
|
v-model="settings.appName"
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-input
|
<q-input
|
||||||
|
dense
|
||||||
:readonly="!user.isPermittedTo('settings', 'write')"
|
:readonly="!user.isPermittedTo('settings', 'write')"
|
||||||
:class="[
|
:class="[colorGroup ? 'col-md-4' : 'col-md-12', 'q-pa-md']"
|
||||||
colorGroup ? 'col-md-4' : 'col-md-3',
|
|
||||||
colorGroup ? 'col-md-6' : 'col-md-6',
|
|
||||||
colorGroup ? 'col-md-4' : 'col-md-12',
|
|
||||||
'q-pa-md',
|
|
||||||
]"
|
|
||||||
filled
|
filled
|
||||||
:label="$t('icon')"
|
:label="$t('icon')"
|
||||||
v-model="settings.icon"
|
v-model="settings.icon"
|
||||||
@@ -36,13 +28,9 @@
|
|||||||
<p class="text-bold text-h6 text-primary q-pa-md">{{ $t('database') }}</p>
|
<p class="text-bold text-h6 text-primary q-pa-md">{{ $t('database') }}</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-input
|
<q-input
|
||||||
|
dense
|
||||||
:readonly="!user.isPermittedTo('settings', 'write')"
|
:readonly="!user.isPermittedTo('settings', 'write')"
|
||||||
:class="[
|
:class="[colorGroup ? 'col-md-4' : 'col-md-12', 'q-pa-md']"
|
||||||
colorGroup ? 'col-md-4' : 'col-md-3',
|
|
||||||
colorGroup ? 'col-md-6' : 'col-md-6',
|
|
||||||
colorGroup ? 'col-md-4' : 'col-md-12',
|
|
||||||
'q-pa-md',
|
|
||||||
]"
|
|
||||||
filled
|
filled
|
||||||
:label="$t('databaseName')"
|
:label="$t('databaseName')"
|
||||||
v-model="settings.databaseName"
|
v-model="settings.databaseName"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-select
|
<q-select
|
||||||
ref="selectRef"
|
ref="selectRef"
|
||||||
|
:label="props.label || ''"
|
||||||
v-model="modelValueLocal"
|
v-model="modelValueLocal"
|
||||||
:options="filteredOptions"
|
:options="filteredOptions"
|
||||||
:option-label="optionLabel"
|
:option-label="optionLabel"
|
||||||
@@ -43,6 +44,9 @@ const props = defineProps({
|
|||||||
type: Array as PropType<T[]>,
|
type: Array as PropType<T[]>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
optionLabel: {
|
optionLabel: {
|
||||||
type: [Function, String] as PropType<((option: T) => string) | string | undefined>,
|
type: [Function, String] as PropType<((option: T) => string) | string | undefined>,
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
8
src/vueLib/models/pageDefaults.ts
Normal file
8
src/vueLib/models/pageDefaults.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { Group } from './group';
|
||||||
|
|
||||||
|
export type PageDefault = {
|
||||||
|
groups?: Group[];
|
||||||
|
selectedColumnFilter?: string;
|
||||||
|
selectedColumnOptions?: string[];
|
||||||
|
weekdays?: number[];
|
||||||
|
};
|
||||||
7
src/vueLib/models/report.ts
Normal file
7
src/vueLib/models/report.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export type Amount = {
|
||||||
|
name: string;
|
||||||
|
events: number;
|
||||||
|
minimal: number;
|
||||||
|
average: number;
|
||||||
|
maximal: number;
|
||||||
|
};
|
||||||
@@ -66,6 +66,7 @@ import { ref } from 'vue';
|
|||||||
import { useAttendeesTable } from './AttendeesTable';
|
import { useAttendeesTable } from './AttendeesTable';
|
||||||
import { useMemberTable } from '../members/MembersTable';
|
import { useMemberTable } from '../members/MembersTable';
|
||||||
import { getLocalPageDefaults } from 'src/localstorage/localStorage';
|
import { getLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pageDefaults';
|
||||||
|
|
||||||
//use constants and function of imports
|
//use constants and function of imports
|
||||||
const { attendees, updateAttendees } = useAttendeesTable();
|
const { attendees, updateAttendees } = useAttendeesTable();
|
||||||
@@ -95,8 +96,10 @@ const open = async (eventArray: number, event: Event) => {
|
|||||||
await updateMembers(event.attendees);
|
await updateMembers(event.attendees);
|
||||||
|
|
||||||
// set custom filter
|
// set custom filter
|
||||||
const defaults = getLocalPageDefaults('attendance');
|
const settings = getLocalPageDefaults('attendance') as PageDefault;
|
||||||
setNewFilter(defaults?.filteredColumn || '', ...(defaults?.filteredValue ?? []));
|
if (settings) {
|
||||||
|
setNewFilter(settings.selectedColumnFilter || '', ...(settings.selectedColumnOptions ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
// set amount of missing attendace
|
// set amount of missing attendace
|
||||||
missingAttendanceAmount.value = filteredMembers.value.length;
|
missingAttendanceAmount.value = filteredMembers.value.length;
|
||||||
|
|||||||
@@ -209,12 +209,13 @@ import AddToEvent from 'src/components/AddToEvent.vue';
|
|||||||
import { databaseName } from 'src/vueLib/models/settings';
|
import { databaseName } from 'src/vueLib/models/settings';
|
||||||
import { useUserStore } from 'src/vueLib/login/userStore';
|
import { useUserStore } from 'src/vueLib/login/userStore';
|
||||||
import { i18n } from 'src/boot/lang';
|
import { i18n } from 'src/boot/lang';
|
||||||
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
|
||||||
import type { Responsible } from 'src/vueLib/models/responsible';
|
import type { Responsible } from 'src/vueLib/models/responsible';
|
||||||
import type { QTableColumn } from 'quasar';
|
import type { QTableColumn } from 'quasar';
|
||||||
import SearchableInput from '../components/SearchableInput.vue';
|
import SearchableInput from '../components/SearchableInput.vue';
|
||||||
import FilterSelect from '../components/FilterSelect.vue';
|
import FilterSelect from '../components/FilterSelect.vue';
|
||||||
import TopButtonGroup from '../components/TopButtonGroup.vue';
|
import TopButtonGroup from '../components/TopButtonGroup.vue';
|
||||||
|
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pageDefaults';
|
||||||
|
|
||||||
const inProps = defineProps({
|
const inProps = defineProps({
|
||||||
addAttendees: { type: Boolean },
|
addAttendees: { type: Boolean },
|
||||||
@@ -285,9 +286,11 @@ onMounted(() => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
localCompareMembers.value = inProps.compareMembers;
|
localCompareMembers.value = inProps.compareMembers;
|
||||||
|
|
||||||
const defaults = getLocalPageDefaults(page.value);
|
const settings = getLocalPageDefaults(page.value) as PageDefault;
|
||||||
selectedColumnFilter.value = defaults?.filteredColumn || '';
|
if (settings) {
|
||||||
selectedColumnOptions.value = defaults?.filteredValue ?? [];
|
selectedColumnFilter.value = settings.selectedColumnFilter || '';
|
||||||
|
selectedColumnOptions.value = settings.selectedColumnOptions || [];
|
||||||
|
}
|
||||||
|
|
||||||
// set custom filter
|
// set custom filter
|
||||||
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
|
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
|
||||||
@@ -369,7 +372,11 @@ function setColumnOptions(columnName: string) {
|
|||||||
|
|
||||||
async function filterMembers() {
|
async function filterMembers() {
|
||||||
setNewFilter(selectedColumnFilter.value, ...(selectedColumnOptions.value || []));
|
setNewFilter(selectedColumnFilter.value, ...(selectedColumnOptions.value || []));
|
||||||
setLocalPageDefaults(page.value, selectedColumnFilter.value, selectedColumnOptions.value || []);
|
const settings = <PageDefault>{
|
||||||
|
selectedColumnFilter: selectedColumnFilter.value,
|
||||||
|
selectedColumnOptions: selectedColumnOptions.value,
|
||||||
|
};
|
||||||
|
setLocalPageDefaults(page.value, settings);
|
||||||
await updateTable();
|
await updateTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +452,8 @@ async function addMemberTo() {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
NotifyResponse(err, 'error');
|
NotifyResponse(err, 'error');
|
||||||
});
|
})
|
||||||
|
.finally(() => (selected.value = []));
|
||||||
|
|
||||||
if (inProps.addAttendees) {
|
if (inProps.addAttendees) {
|
||||||
await updateMemberLastVisit(selected.value);
|
await updateMemberLastVisit(selected.value);
|
||||||
|
|||||||
Reference in New Issue
Block a user