Work on badge printing and the actual badge layout.

This commit is contained in:
Scott Idem
2025-10-09 19:26:35 -04:00
parent 0f05fd708f
commit 74cc5c5d0d
7 changed files with 811 additions and 79 deletions

312
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "osit-aether-app-svelte",
"version": "3.3.0",
"version": "3.7.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "osit-aether-app-svelte",
"version": "3.3.0",
"version": "3.7.1",
"dependencies": {
"@codemirror/commands": "^6.8.1",
"@codemirror/gutter": "^0.19.9",
@@ -36,6 +36,7 @@
"lucide-svelte": "0.*.0",
"marked": "^16.0.0",
"openai": "^5.20.1",
"qrcode": "^1.5.4",
"shadcn-svelte": "^1.0.0",
"svelte-persisted-store": "^0.12.0"
},
@@ -74,6 +75,7 @@
"@tiptap/starter-kit": "^2.10.3",
"@types/eslint": "^9.0.0",
"@types/node": "^24.0.0",
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"bits-ui": "^2.0.0",
@@ -2357,6 +2359,7 @@
"integrity": "sha512-5JJBPu3U2KXpRwc+e/D2Pl+DJM9oBcCl6XtWenrb6xc6H4lFa0XIJaSch4wMiADrhX512sVIUf13VnEp7aWO1w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@sveltejs/acorn-typescript": "^1.0.5",
@@ -2396,6 +2399,7 @@
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"debug": "^4.4.1",
@@ -2456,6 +2460,7 @@
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.4.tgz",
"integrity": "sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Fuzzyma"
@@ -2479,6 +2484,7 @@
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.select.js/-/svg.select.js-4.0.3.tgz",
"integrity": "sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 14.18"
},
@@ -2492,7 +2498,6 @@
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.8.0"
}
@@ -2793,6 +2798,7 @@
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.26.1.tgz",
"integrity": "sha512-fymyd/XZvYiHjBoLt1gxs024xP/LY26d43R1vluYq7AHBL/7DE3ywzy+1GEsGyAv5Je2L0KBhNIR/izbq3Kaqg==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@@ -2835,6 +2841,7 @@
"integrity": "sha512-oHevUcZbTMFOTpdCEo4YEDe044MB4P1ZrWyML8CGe5tnnKdlI9BN03AXpI1mEEa5CA3H1/eEckXx8EiCgYwQ3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"tippy.js": "^6.3.7"
},
@@ -2880,6 +2887,7 @@
"integrity": "sha512-/TDDOwONl0qEUc4+B6V9NnWtSjz95eg7/8uCb8Y8iRbGvI9vT4/znRKofFxstvKmW4URu/H74/g0ywV57h0B+A==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@@ -3304,6 +3312,7 @@
"integrity": "sha512-t9Nc/UkrbCfnSHEUi1gvUQ2ZPzvfdYFT5TExoV2DTiUCkhG6+mecT5bTVFGW3QkPmbToL+nFhGn4ZRMDD0SP3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
@@ -3345,6 +3354,7 @@
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.26.1.tgz",
"integrity": "sha512-8aF+mY/vSHbGFqyG663ds84b+vca5Lge3tHdTMTKazxCnhXR9dn2oQJMnZ78YZvdRbkPkMJJHti9h3K7u2UQvw==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-changeset": "^2.3.0",
"prosemirror-collab": "^1.3.1",
@@ -3490,10 +3500,21 @@
"integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.10.0"
}
},
"node_modules/@types/qrcode": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz",
"integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -3543,6 +3564,7 @@
"integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.43.0",
"@typescript-eslint/types": "8.43.0",
@@ -4304,6 +4326,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4338,11 +4361,19 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -4514,6 +4545,15 @@
"node": ">=6"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/chai": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
@@ -4583,6 +4623,17 @@
"node": ">=18"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -4611,7 +4662,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -4624,7 +4674,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/colord": {
@@ -4761,6 +4810,15 @@
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/dedent-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
@@ -4849,6 +4907,12 @@
"integrity": "sha512-OSeyyWOUetDy9oFWeddJgi83OnRA3hSFh3RrbltmPgqHszE9f24eUCVLI4mPg0ifsWk0lQTdnS+jyGNrPMvhDA==",
"license": "Apache-2.0"
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -4863,6 +4927,12 @@
"node": ">= 0.4"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -4999,6 +5069,7 @@
"integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -5599,6 +5670,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -5742,6 +5822,7 @@
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"dev": true,
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=12.0.0"
}
@@ -5828,6 +5909,15 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6285,6 +6375,7 @@
"integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/hast": "^3.0.0",
"devlop": "^1.0.0",
@@ -6696,6 +6787,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -6724,7 +6824,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -6813,6 +6912,15 @@
"node": ">=18"
}
},
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -6832,6 +6940,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -6965,6 +7074,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -7098,6 +7208,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.3.tgz",
"integrity": "sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"orderedmap": "^2.0.0"
}
@@ -7127,6 +7238,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-transform": "^1.0.0",
@@ -7175,6 +7287,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.0.tgz",
"integrity": "sha512-FatMIIl0vRHMcNc3sPy3cMw5MMyWuO1nWQxqvYpJvXAruucGvmQ2tyyjT2/Lbok77T9a/qZqBVCq4sj43V2ihw==",
"license": "MIT",
"peer": true,
"dependencies": {
"prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0",
@@ -7206,6 +7319,23 @@
"node": ">=6"
}
},
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -7241,6 +7371,21 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC"
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -7287,6 +7432,7 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz",
"integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -7773,6 +7919,12 @@
"node": ">=10"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC"
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
@@ -7862,6 +8014,32 @@
"dev": true,
"license": "MIT"
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -7934,6 +8112,7 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.10.tgz",
"integrity": "sha512-UY+OhrWK7WI22bCZ00P/M3HtyWgwJPi9IxSRkoAE2MeAy6kl7ZlZWJZ8RaB+X4KD/G+wjis+cGVnVYaoqbzBqg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -8187,6 +8366,7 @@
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
"integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
"license": "MIT",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
@@ -8216,7 +8396,8 @@
"version": "4.1.13",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz",
"integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
@@ -8390,6 +8571,7 @@
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -8451,6 +8633,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz",
"integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -8672,6 +8855,12 @@
"node": ">= 8"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC"
},
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
@@ -8699,6 +8888,26 @@
"node": ">=0.10.0"
}
},
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC"
},
"node_modules/yallist": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
@@ -8708,6 +8917,93 @@
"node": ">=18"
}
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/yargs/node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yargs/node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "osit-aether-app-svelte",
"version": "3.7.1",
"version": "3.8.0",
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
"homepage": "https://oneskyit.com/",
"private": true,
@@ -53,6 +53,7 @@
"@tiptap/starter-kit": "^2.10.3",
"@types/eslint": "^9.0.0",
"@types/node": "^24.0.0",
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"bits-ui": "^2.0.0",
@@ -113,6 +114,7 @@
"lucide-svelte": "0.*.0",
"marked": "^16.0.0",
"openai": "^5.20.1",
"qrcode": "^1.5.4",
"shadcn-svelte": "^1.0.0",
"svelte-persisted-store": "^0.12.0"
}

View File

@@ -34,6 +34,7 @@ import {
import {
generate_qr_code,
js_generate_qr_code,
} from "$lib/ae_core/core__qr_code";
import {
@@ -697,6 +698,7 @@ let export_obj = {
handle_update_ae_obj_id_crud: handle_update_ae_obj_id_crud,
update_ae_obj_id_crud_v2: update_ae_obj_id_crud_v2,
handle_download_export__obj_type: handle_download_export__obj_type,
generate_qr_code: generate_qr_code
generate_qr_code: generate_qr_code,
js_generate_qr_code: js_generate_qr_code,
};
export let core_func = export_obj;

View File

@@ -1,3 +1,5 @@
import QRCode from 'qrcode'
import type { key_val } from '$lib/ae_stores';
import { api } from '$lib/api';
@@ -136,4 +138,103 @@ export async function generate_qr_code(
// If not returning a blob, return the raw API response
return ae_promises.generate_qr_code;
}
// Updated 2025-10-09
/**
* Generates a QR code image as a Base64 data URL based on provided parameters.
*
* NOTE: This function handles the data formatting and QR generation,
* but does NOT handle server-side logic like file saving, disk path checks,
* or database lookups (like redis_lookup_id_random).
*
* @param {string} qr_type - The type of data format ('mecard', 'vcard', 'obj', 'kv', 'js', 'str').
* @param {Object} params - An object containing all necessary query parameters (n, fn, url, email, etc.).
* @param {string} qr_id - A unique ID (unused in pure client-side generation, but kept for context).
* @returns {Promise<string>} A promise that resolves to a Base64 data URL of the QR code image.
* @throws {Error} If the qr_type is unknown or data is missing.
*/
export async function js_generate_qr_code(qr_type, params = {}) {
const {
n = '', fn = '', org = '', url = '', email = '', tel = '',
adr_poa = '', adr_ext = '', adr_str = '', adr_loc = '', adr_reg = '', adr_postal = '', adr_country = '',
obj_type, obj_id, key, val, js, str
} = params;
console.log(`*** js_generate_qr_code() *** qr_type=${qr_type}`, params);
let qrData = null;
// --- 1. Replicate Data Formatting Logic ---
switch (qr_type) {
case 'mecard':
// Format: MECARD:N:name;EMAIL:email;ADR:address;;
// Note: The original Python code had adr, but we'll use n and email as a minimum.
qrData = `MECARD:N:${n};EMAIL:${email};;`;
// You can enhance this with other MeCard fields if needed.
break;
case 'vcard':
// Format: BEGIN:VCARD...END:VCARD
qrData = `BEGIN:VCARD\nVERSION:3.0\nN:${n}\nFN:${fn}\nORG:${org}\nEMAIL:${email}\n`;
if (url) { qrData += `URL:${url}\n`; }
if (tel) { qrData += `TEL:${tel}\n`; }
if (adr_loc) {
// ADR format: TYPE=postal:Po box;Ext;Street;Locality;Region;Postal code;Country
qrData += `ADR:${adr_poa};${adr_ext};${adr_str};${adr_loc};${adr_reg};${adr_postal};${adr_country}\n`;
}
qrData += 'END:VCARD';
break;
case 'obj':
// Custom format: OBJ:ot:obj_type,oi:obj_id
if (!obj_type || !obj_id) throw new Error('Missing obj_type or obj_id for type "obj".');
qrData = `OBJ:ot:${obj_type},oi:${obj_id}`;
break;
case 'kv':
// Custom format: KV:k:"key",v:"val"
if (!key || !val) throw new Error('Missing key or val for type "kv".');
qrData = `KV:k:"${key}",v:"${val}"`;
break;
case 'js':
// Assumes 'js' is a stringified JSON object
if (!js) throw new Error('Missing js string for type "js".');
qrData = `JS:${js}`;
break;
case 'str':
// Raw string data
if (!str) throw new Error('Missing raw string data for type "str".');
qrData = str;
break;
default:
throw new Error(`Unknown QR type: ${qr_type}`);
}
if (!qrData) {
throw new Error('Failed to create QR code data string.');
}
// --- 2. Generate QR Code Image ---
try {
// Options match the Python 'qrcode' library defaults closely:
// error_correction = qrcode.constants.ERROR_CORRECT_M
// box_size = 10, border = 1
const dataUrl = await QRCode.toDataURL(qrData, {
errorCorrectionLevel: 'M',
margin: 1, // Corresponds to border
scale: 10, // Corresponds to box_size
type: 'image/png',
});
console.log('Generated QR code data URL:', dataUrl);
return dataUrl;
} catch (error) {
console.error('Error generating QR code:', error);
throw new Error('Could not generate QR code image.');
}
}

View File

@@ -18,14 +18,55 @@ export const properties_to_save = [
'name',
'description',
'template_code',
'template_type',
'template_json',
'template_svg',
'template_css',
'template_html',
// 'template_code',
// 'template_type',
// 'template_json',
// 'template_svg',
// 'template_css',
// 'template_html',
'default',
'logo_filename',
'logo_path',
'header_path',
'secondary_header_path',
'footer_path',
'header_row_1',
'header_row_2',
// 'footer_title',
// 'footer_left',
// 'footer_right',
// 'header_background',
// 'footer_background',
'badge_type_list',
'ticket_list',
'ticket_1_text',
'ticket_2_text',
'ticket_3_text',
'ticket_4_text',
'ticket_5_text',
'ticket_6_text',
'ticket_7_text',
'ticket_8_text',
// 'ticket_9_text',
// 'ticket_10_text',
'wireless_ssid',
'wireless_password',
'show_qr_front',
'show_qr_back',
'layout',
'style_filename',
// 'style_href',
// 'default',
'enable',
'hide',
'priority',
@@ -70,14 +111,38 @@ export async function process_ae_obj__event_badge_template_props({
name: obj.name,
description: obj.description,
template_code: obj.template_code,
template_type: obj.template_type,
template_json: obj.template_json,
template_svg: obj.template_svg,
template_css: obj.template_css,
template_html: obj.template_html,
logo_filename: obj.logo_filename,
logo_path: obj.logo_path,
default: obj.default,
header_path: obj.header_path,
secondary_header_path: obj.secondary_header_path,
footer_path: obj.footer_path,
header_row_1: obj.header_row_1,
header_row_2: obj.header_row_2,
badge_type_list: obj.badge_type_list,
ticket_list: obj.ticket_list,
ticket_1_text: obj.ticket_1_text,
ticket_2_text: obj.ticket_2_text,
ticket_3_text: obj.ticket_3_text,
ticket_4_text: obj.ticket_4_text,
ticket_5_text: obj.ticket_5_text,
ticket_6_text: obj.ticket_6_text,
ticket_7_text: obj.ticket_7_text,
ticket_8_text: obj.ticket_8_text,
wireless_ssid: obj.wireless_ssid,
wireless_password: obj.wireless_password,
show_qr_front: obj.show_qr_front,
show_qr_back: obj.show_qr_back,
layout: obj.layout,
style_filename: obj.style_filename,
// default: obj.default,
enable: obj.enable,
hide: obj.hide,
priority: obj.priority,

View File

@@ -198,7 +198,55 @@ export interface Badge_template {
event_id: string;
event_id_random: string;
name: string;
description?: null|string;
logo_filename?: null|string;
logo_path?: null|string;
header_path?: null|string;
secondary_header_path?: null|string;
footer_path?: null|string;
header_row_1?: null|string;
header_row_2?: null|string;
badge_type_list?: any; // This is an array of objects with code and name
badge_type_info_kv?: any; // This is a key value list with code as the key and name as the value. Use this for exhibitor, presenter, attendee, staff, vip, etc.
other_info_kv?: any; // This is a key value list with code as the key and name as the value. Use this for other custom fields.
ticket_list?: any; // This is an array of objects with num, code, and name
ticket_1_text?: null|string;
ticket_2_text?: null|string;
ticket_3_text?: null|string;
ticket_4_text?: null|string;
ticket_5_text?: null|string;
ticket_6_text?: null|string;
ticket_7_text?: null|string;
ticket_8_text?: null|string;
wireless_ssid?: null|string;
wireless_password?: null|string;
show_qr_front: boolean;
show_qr_back: boolean;
layout?: null|string;
style_filename?: null|string;
enable: null|boolean;
hide?: null|boolean;
priority?: null|boolean
sort?: null|number;
group?: null|string;
notes?: null|string;
created_on: Date;
updated_on?: null|Date;
// Generated fields for sorting locally only
// Additional fields for convenience (database views)
}
// Updated 2024-10-16

View File

@@ -18,33 +18,34 @@ let {
}: Props = $props();
// *** Import Svelte specific
// import { browser } from '$app/environment';
import { browser } from '$app/environment';
// *** Import other supporting libraries
import { liveQuery } from "dexie";
import {
ArrowDown01, ArrowDown10, ArrowDownUp,
BetweenVerticalEnd, BetweenVerticalStart,
BookHeart, BookImage, Bookmark, BookOpenText, BriefcaseBusiness,
Check, Copy,
Expand, Eye, EyeOff,
Flag, FlagOff, FilePlus, Fingerprint,
Globe,
Library,
MessageSquareWarning, Minus,
Notebook,
Pencil, Plus,
RemoveFormatting,
SquareLibrary,
Shapes, Share2, ShieldCheck, ShieldMinus, Siren, Skull,
Tags, Target, ToggleLeft, ToggleRight, Trash2, TypeOutline,
X
} from '@lucide/svelte';
// import {
// ArrowDown01, ArrowDown10, ArrowDownUp,
// BetweenVerticalEnd, BetweenVerticalStart,
// BookHeart, BookImage, Bookmark, BookOpenText, BriefcaseBusiness,
// Check, Copy,
// Expand, Eye, EyeOff,
// Flag, FlagOff, FilePlus, Fingerprint,
// Globe,
// Library,
// MessageSquareWarning, Minus,
// Notebook,
// Pencil, Plus,
// RemoveFormatting,
// SquareLibrary,
// Shapes, Share2, ShieldCheck, ShieldMinus, Siren, Skull,
// Tags, Target, ToggleLeft, ToggleRight, Trash2, TypeOutline,
// X
// } from '@lucide/svelte';
import type { key_val } from '$lib/ae_stores';
// import { ae_util } from '$lib/ae_utils/ae_utils';
import { core_func } from '$lib/ae_core/ae_core_functions';
import { ae_snip, ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
import { db_events } from "$lib/ae_events/db_events";
import { events_loc, events_sess, events_slct, events_trigger } from '$lib/ae_events_stores';
@@ -147,9 +148,9 @@ let option_ticket_3_override: any = $state(null);
let slct_badge_type = '';
let qr_type = 'mecard';
let qr_img_src = $state(null);
let img_obj_url = $state(null);
// let qr_type = 'mecard';
// let qr_img_src = $state(null);
// let img_obj_url = $state(null);
/* *** BEGIN *** This should be moved out */
@@ -199,6 +200,36 @@ let lq__event_badge_template_obj = $derived(liveQuery(async () => {
full_name_override = $lq__event_badge_obj?.full_name_override ?? $lq__event_badge_obj?.full_name;
// if (full_name_override) {
// let name_parts = full_name_override.trim().split(' ');
// longest_full_name_override_part = 0;
// for (let part of name_parts) {
// if (part.length > longest_full_name_override_part) {
// longest_full_name_override_part = part.length;
// }
// }
// } else {
// longest_full_name_override_part = 0;
// }
professional_title_override = $lq__event_badge_obj?.professional_title_override ?? $lq__event_badge_obj?.professional_title;
affiliations_override = $lq__event_badge_obj?.affiliations_override ?? $lq__event_badge_obj?.affiliations;
location_override = $lq__event_badge_obj?.location_override ?? $lq__event_badge_obj?.location;
option_other_1_override = $lq__event_badge_obj?.option_other_1_override ?? $lq__event_badge_obj?.option_other_1;
option_other_2_override = $lq__event_badge_obj?.option_other_2_override ?? $lq__event_badge_obj?.option_other_2;
option_ticket_1_override = $lq__event_badge_obj?.option_ticket_1_override ?? $lq__event_badge_obj?.option_ticket_1;
option_ticket_2_override = $lq__event_badge_obj?.option_ticket_2_override ?? $lq__event_badge_obj?.option_ticket_2;
option_ticket_3_override = $lq__event_badge_obj?.option_ticket_3_override ?? $lq__event_badge_obj?.option_ticket_3;
// option_ticket_4_override = $lq__event_badge_obj?.option_ticket_4_override ?? $lq__event_badge_obj?.option_ticket_4;
// option_ticket_5_override = $lq__event_badge_obj?.option_ticket_5_override ?? $lq__event_badge_obj?.option_ticket_5;
// option_ticket_6_override = $lq__event_badge_obj?.option_ticket_6_override ?? $lq__event_badge_obj?.option_ticket_6;
// option_ticket_7_override = $lq__event_badge_obj?.option_ticket_7_override ?? $lq__event_badge_obj?.option_ticket_7;
// option_ticket_8_override = $lq__event_badge_obj?.option_ticket_8_override ?? $lq__event_badge_obj?.option_ticket_8;
return results;
}));
@@ -211,6 +242,11 @@ function preventDefault(fn) {
}
let qr_data_url: any = $state('');
let qr_error_message = $state('');
// Trigger doing a update for event badge
$effect(() => {
if (ae_triggers.event_badge_update) {
@@ -219,16 +255,69 @@ if (ae_triggers.event_badge_update) {
update_complete = false;
}
});
$effect(async () => {
if (browser && $lq__event_badge_obj?.event_badge_id) {
console.log('Generating QR code...');
qr_error_message = '';
qr_data_url = '';
let params: any = {};
params.obj_type = 'event_badge';
params.obj_id = $lq__event_badge_obj?.event_badge_id;
try {
// Use 'vcard' as the qr_type, passing all required params
// const data_url = await core_func.js_generate_qr_code('obj', params);
qr_data_url = core_func.js_generate_qr_code('obj', params);
} catch (error) {
qr_error_message = error.message;
console.error(error);
}
}
});
// Example VCard data
const qrParams = {
n: 'Doe, John',
fn: 'John Doe',
org: 'Acme Corp',
email: 'john.doe@example.com',
tel: '+15551234567',
adr_loc: 'Springfield', // Must be present to include address block
adr_country: 'USA'
// ... include all other necessary parameters
};
/**
* Function to call the QR generation logic
*/
async function get_qr_code() {
console.log('Generating QR code...');
qr_error_message = '';
qr_data_url = '';
try {
// Use 'vcard' as the qr_type, passing all required params
const dataUrl = await core_func.js_generate_qr_code('vcard', qrParams);
qr_data_url = dataUrl;
} catch (error) {
qr_error_message = error.message;
console.error(error);
}
}
</script>
<pre class="whitespace-pre-wrap break-words text-xs">
{JSON.stringify($lq__event_badge_obj, null, 2)}
</pre>
<!--
onclick={() => {
@@ -242,19 +331,119 @@ onkeypress={() => {
// slct_this_badge = true
}} -->
<!-- <button class="btn btn-sm preset-outlined-surface-200-800" onclick={get_qr_code}>Generate VCard QR Code</button> -->
<!-- {#if qr_error_message}
<p style="color: red;">Error: {qr_error_message}</p>
{:else if qr_data_url}
<h2>Generated QR Code:</h2>
<img src={qr_data_url} alt="QR Code" />
{:else}
<p>Click the button to generate the QR code.</p>
{/if} -->
<section
class="event_badge_wrapper event_badge print_area outline"
id="event_badge_{$lq__event_badge_obj?.event_badge_id}"
class="event_badge_wrapper event_badge print_area
flex flex-row flex-wrap gap-4
items-stretch justify-center
p-2 m-0
outline-2 outline-dashed outline-blue-500
"
>
{#if $lq__event_badge_obj && $lq__event_badge_template_obj}
<!-- *** badge_front section start *** -->
<section class="badge_front badge_type__{use_badge_type_code.toLowerCase()}">
<section
class="badge_front badge_type__{use_badge_type_code.toLowerCase()}
max-w-lg
p-0 m-0
text-center
relative
outline-2 outline-red-500/50 hover:outline-red-700/75
group
"
>
<span
class="
print:hidden absolute top-1 left-4
text-xs italic
text-gray-500 group-hover:text-red-800
transition-all
"
>
Front of badge
</span>
<div
class="
print:hidden absolute top-1 right-4
hover:preset-tonal-secondary
transition-all group
flex flex-col gap-1 items-center justify-center
"
class:preset-outlined-warning-200-800={$ae_loc.edit_mode}
class:preset-tonal-warning={$ae_loc.edit_mode}
>
<button
class="
btn btn-sm text-xs
preset-tonal-warning preset-outlined-warning-100-900 hover:preset-filled-secondary-500
transition-all group
"
onclick={() => {
show_event_badge_tools_modal = true;
$ae_loc.edit_mode = !$ae_loc.edit_mode;
}}
title="Edit Badge Information"
>
{#if $ae_loc.edit_mode}
<span class="fas fa-times m-1"></span>
{:else}
<span class="fas fa-edit m-1"></span>
{/if}
<span
class="
hidden
group-hover:inline-block
text-xs
"
>
{#if $ae_loc.edit_mode}
Close Edit
{:else}
Edit Badge Information
{/if}
</span>
</button>
<div
class="w-md max-w-lg m-1 p-1"
class:hidden={!$ae_loc.edit_mode}>
<p class="text-xs italic text-gray-500">
Show list of fields that they can edit here. This may need to broken down in to sections that can be collapsed.
</p>
<ul class="text-left list-disc list-inside text-sm">
<li>Full Name</li>
<li>Professional Title</li>
<li>Affiliations</li>
<li>Location</li>
<li>Option Ticket 1</li>
<li>Option Ticket 2</li>
<li>Option Ticket 3</li>
<li>Option Other 1</li>
<li>Option Other 2</li>
<li>Badge Type</li>
<li>Allow Tracking</li>
<li>Show Print Message</li>
<li>Hide QR Code</li>
</ul>
</div>
</div>
{#if $lq__event_badge_template_obj.header_path}
<div class="badge_header image">
<div class="badge_header image max-w-xl">
<img class="header_image" src="{$lq__event_badge_template_obj.header_path}" alt="check header path">
</div>
{:else}
@@ -267,13 +456,13 @@ onkeypress={() => {
</div>
{/if}
<div class="badge_body">
<div class="badge_body space-y-1">
<div class="person_name"
>
<!-- NOTE: Need to add some logic if any part of the name is 9 characters or more to reduce the font size OR if the total length is more than 15ish to reduce the font size :NOTE -->
<!-- Examples: mSC-tTzL_OA, QLddtYl8sfo -->
<div class="full_name_override_all"
<div class="full_name_override_all text-6xl"
class:str_05={longest_full_name_override_part==5}
class:str_06={longest_full_name_override_part==6}
class:str_07={longest_full_name_override_part==7}
@@ -303,7 +492,7 @@ onkeypress={() => {
</div>
{#if professional_title_override}
<div class="professional_title"
<div class="professional_title text-2xl"
class:str_05={professional_title_override.length==5}
class:str_06={professional_title_override.length==6}
class:str_07={professional_title_override.length==7}
@@ -340,7 +529,7 @@ onkeypress={() => {
{#if affiliations_override || location_override}
<div class="affiliations_location">
{#if affiliations_override}
<div class="affiliations"
<div class="affiliations text-4xl"
class:str_05={affiliations_override.length==5}
class:str_06={affiliations_override.length==6}
class:str_07={affiliations_override.length==7}
@@ -363,7 +552,7 @@ onkeypress={() => {
{/if}
{#if location_override}
<div class="location"
<div class="location text-2xl"
class:str_15={location_override.length>=15}
class:str_20={location_override.length>=20}
class:str_25={location_override.length>=25}
@@ -393,11 +582,11 @@ onkeypress={() => {
{/if}
</span>
{#if $lq__event_badge_template_obj.show_qr_front}
{#await initial_loading_promise}
{#await qr_data_url}
Generating...
{:then result}
{#if initial_loading_promise}
<img class="qr_code mecard_qr" style="" src="/event/qr_image/event_badge_mecard_{$lq__event_badge_obj.event_badge_id_random}?qr_filename=attendee_qr.png" alt="missing QR code">
{#if qr_data_url}
<img class="qr_code mecard_qr" style="" src={qr_data_url} alt="missing QR code">
{/if}
{/await}
{/if}
@@ -458,7 +647,7 @@ onkeypress={() => {
class:v_hide_print={hide_qr}
src={qr_img_src}
src={qr_data_url}
alt="missing QR code"
ondblclick={() => {
@@ -477,14 +666,33 @@ onkeypress={() => {
<!-- *** badge_back (fold under) section start *** -->
<section class="badge_back">
<section
class="badge_back
max-w-lg
p-0 m-0
text-left
relative
outline-2 outline-green-500/50 hover:outline-green-700/75
group
"
>
<span
class="
print:hidden absolute top-1 right-4
text-xs italic
text-gray-500 group-hover:text-green-800
"
>
Back of badge
</span>
{#if $lq__event_badge_template_obj.secondary_header_path}
<div class="badge_back_header image">
<div class="badge_back_header image max-w-xl">
<img class="header_image" src="{$lq__event_badge_template_obj.secondary_header_path}" alt="check secondary header path">
</div>
{:else if $lq__event_badge_template_obj.header_path}
<div class="badge_back_header image">
<div class="badge_back_header image max-w-xl">
<img class="header_image" src="{$lq__event_badge_template_obj.header_path}" alt="check primary header path">
</div>
{:else}
@@ -533,24 +741,25 @@ onkeypress={() => {
{$lq__event_badge_obj.given_name}'s
QR Code and Badge ID:
</div>
{#await event_badge_qr_id_get_promise}
{#await qr_data_url}
<!-- Must use this await when the image is generated. It either happens here or on the $effect with async -->
Generating...
{:then result}
{#if event_badge_qr_id_get_promise}
<div class="qr_badge_id_part_1">
{#if result}
<div class="qr_badge_id_part_1 flex flex-row items-center justify-between">
<img
class="qr_code mecard_qr"
class="qr_code mecard_qr max-w-48 hover:scale-200 transition-transform duration-200 ease-in-out hover:p-4 hover:border-2 hover:border-red-500 hover:bg-white"
class:v_hide_print={hide_qr}
src={qr_img_src}
src={result}
alt="badge QR code"
ondblclick={() => {
// (hide_qr) ? hide_qr = !hide_qr : hide_qr;
(hide_qr) ? hide_qr = false : hide_qr = true;
}}
/>
<div class="qr_badge_id_agreement fs_xs f_align_justify">
<div class="qr_badge_id_agreement fs_xs f_align_justify text-xs">
{#if allow_tracking}
<p>This was <strong>allowed</strong> at the time your badge was printed. You may opt-out at anytime.</p>
<p>By allowing this QR code to be scanned by an exhibitor and/or industry supporter, you understand and agree that they may use your personal information.</p>
@@ -570,7 +779,7 @@ onkeypress={() => {
</div>
{/if}
<div class="container app_information">
<div class="container app_information text-xs">
<strong>Download Meeting App:</strong>
<!-- <img style="width: .75in; float: right;" src="/event/qr_image/testing123" alt="missing person information QR code"> -->
<ol style="margin: 0; padding-top: 0; padding-bottom: 0;">
@@ -664,7 +873,8 @@ onkeypress={() => {
</div>
<div class="badge_back_footer">
<div class="badge_back_footer text-xxs italic text-gray-500">
<!-- This will need to be rotated with CSS for printing on the fanfold badges. -->
Fold in half here
<!-- <ol>
<li>Fold this section under the badge.</li>
@@ -678,10 +888,10 @@ onkeypress={() => {
<!-- *** receipt section start *** -->
<section class="receipt"> <!-- receipt class div start -->
<section class="receipt hidden"> <!-- receipt class div start -->
<div class="receipt_header">
<img class="badge_logo" src="{$lq__event_badge_template_obj.logo_path}" alt="check badge logo">
<img class="badge_logo max-w-sm" src="{$lq__event_badge_template_obj.logo_path}" alt="check badge logo">
<div class="banner_text">
<div class="row_one">{$lq__event_badge_template_obj.header_row_1}</div>
<div class="row_two">{$lq__event_badge_template_obj.header_row_2}</div>
@@ -731,7 +941,7 @@ onkeypress={() => {
<!-- *** ticket section start *** -->
<section class="tickets_left_container">
<section class="tickets_left_container hidden">
<div class="tickets"> <!-- Fold class div start -->
<div class="ticket_container ticket_1">
@@ -761,7 +971,7 @@ onkeypress={() => {
<!-- *** ticket section start *** -->
<section class="tickets_right_container">
<section class="tickets_right_container hidden">
<div class="tickets"> <!-- Fold class div start -->
<div class="ticket_container ticket_5">
@@ -793,3 +1003,11 @@ onkeypress={() => {
{/if} <!-- End if for lq__event_badge_template_obj -->
</section>
<pre class="whitespace-pre-wrap break-words text-xs max-h-24 overflow-auto p-2 bg-surface-200 border border-surface-300 rounded">
{JSON.stringify($lq__event_badge_obj, null, 2)}
</pre>