feat: implement offline queue, background sync and AI robustness

This commit is contained in:
2025-12-17 23:25:12 +01:00
parent fe82d52a85
commit 6f08bb3c4c
70 changed files with 3132 additions and 55 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,5 @@
{
"/page": "app/page.js",
"/bottles/[id]/page": "app/bottles/[id]/page.js"
"/bottles/[id]/page": "app/bottles/[id]/page.js",
"/manifest.webmanifest/route": "app/manifest.webmanifest/route.js"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
self.__RSC_SERVER_MANIFEST="{\n \"node\": {\n \"af8f7c7b0fe98b99031bb1c9e8ef0ed13b2eae40\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\"\n }\n },\n \"9cc2ffbf85f4e72220537b4253faa867d3a48bad\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\"\n }\n },\n \"6a127058a156be3d9a11c57b1d246782770d0176\": {\n \"workers\": {\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n },\n \"e1977f062955b9a8da859aeedf3804faaa120650\": {\n \"workers\": {\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n }\n },\n \"edge\": {},\n \"encryptionKey\": \"4RdBFpcP/vD8bgHhBZz5hrn1hKw9NazGqqaZdOVPnxY=\"\n}"
self.__RSC_SERVER_MANIFEST="{\n \"node\": {\n \"af8f7c7b0fe98b99031bb1c9e8ef0ed13b2eae40\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\",\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\",\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n },\n \"9cc2ffbf85f4e72220537b4253faa867d3a48bad\": {\n \"workers\": {\n \"app/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\",\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/page\": \"action-browser\",\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n },\n \"6a127058a156be3d9a11c57b1d246782770d0176\": {\n \"workers\": {\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n },\n \"e1977f062955b9a8da859aeedf3804faaa120650\": {\n \"workers\": {\n \"app/bottles/[id]/page\": \"(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!\"\n },\n \"layer\": {\n \"app/bottles/[id]/page\": \"action-browser\"\n }\n }\n },\n \"edge\": {},\n \"encryptionKey\": \"4RdBFpcP/vD8bgHhBZz5hrn1hKw9NazGqqaZdOVPnxY=\"\n}"

View File

@@ -2,23 +2,27 @@
"node": {
"af8f7c7b0fe98b99031bb1c9e8ef0ed13b2eae40": {
"workers": {
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!",
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
},
"layer": {
"app/page": "action-browser"
"app/page": "action-browser",
"app/bottles/[id]/page": "action-browser"
}
},
"9cc2ffbf85f4e72220537b4253faa867d3a48bad": {
"workers": {
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
"app/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!",
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
},
"layer": {
"app/page": "action-browser"
"app/page": "action-browser",
"app/bottles/[id]/page": "action-browser"
}
},
"6a127058a156be3d9a11c57b1d246782770d0176": {
"workers": {
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%5D&__client_imported__=true!"
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
},
"layer": {
"app/bottles/[id]/page": "action-browser"
@@ -26,7 +30,7 @@
},
"e1977f062955b9a8da859aeedf3804faaa120650": {
"workers": {
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%5D&__client_imported__=true!"
"app/bottles/[id]/page": "(action-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fupdate-bottle-status.ts%22%2C%5B%22updateBottleStatus%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-tasting.ts%22%2C%5B%22saveTasting%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fanalyze-bottle.ts%22%2C%5B%22analyzeBottle%22%5D%5D%2C%5B%22%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fservices%2Fsave-bottle.ts%22%2C%5B%22saveBottle%22%5D%5D%5D&__client_imported__=true!"
},
"layer": {
"app/bottles/[id]/page": "action-browser"

View File

@@ -181,6 +181,16 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
/***/ }),
/***/ "(ssr)/./node_modules/lucide-react/dist/esm/icons/refresh-cw.js":
/*!****************************************************************!*\
!*** ./node_modules/lucide-react/dist/esm/icons/refresh-cw.js ***!
\****************************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ RefreshCw)\n/* harmony export */ });\n/* harmony import */ var _createLucideIcon_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../createLucideIcon.js */ \"(ssr)/./node_modules/lucide-react/dist/esm/createLucideIcon.js\");\n/**\n * @license lucide-react v0.300.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */ \nconst RefreshCw = (0,_createLucideIcon_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"RefreshCw\", [\n [\n \"path\",\n {\n d: \"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\",\n key: \"v9h5vc\"\n }\n ],\n [\n \"path\",\n {\n d: \"M21 3v5h-5\",\n key: \"1q7to0\"\n }\n ],\n [\n \"path\",\n {\n d: \"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\",\n key: \"3uifl3\"\n }\n ],\n [\n \"path\",\n {\n d: \"M8 16H3v5\",\n key: \"1cv678\"\n }\n ]\n]);\n //# sourceMappingURL=refresh-cw.js.map\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHNzcikvLi9ub2RlX21vZHVsZXMvbHVjaWRlLXJlYWN0L2Rpc3QvZXNtL2ljb25zL3JlZnJlc2gtY3cuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQTs7Ozs7Q0FLQyxHQUVxRDtBQUV0RCxNQUFNQyxZQUFZRCxnRUFBZ0JBLENBQUMsYUFBYTtJQUM5QztRQUFDO1FBQVE7WUFBRUUsR0FBRztZQUFzREMsS0FBSztRQUFTO0tBQUU7SUFDcEY7UUFBQztRQUFRO1lBQUVELEdBQUc7WUFBY0MsS0FBSztRQUFTO0tBQUU7SUFDNUM7UUFBQztRQUFRO1lBQUVELEdBQUc7WUFBdURDLEtBQUs7UUFBUztLQUFFO0lBQ3JGO1FBQUM7UUFBUTtZQUFFRCxHQUFHO1lBQWFDLEtBQUs7UUFBUztLQUFFO0NBQzVDO0FBRStCLENBQ2hDLHNDQUFzQyIsInNvdXJjZXMiOlsid2VicGFjazovL3doaXNreS12YXVsdC8uL25vZGVfbW9kdWxlcy9sdWNpZGUtcmVhY3QvZGlzdC9lc20vaWNvbnMvcmVmcmVzaC1jdy5qcz8yMGVhIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2UgbHVjaWRlLXJlYWN0IHYwLjMwMC4wIC0gSVNDXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgSVNDIGxpY2Vuc2UuXG4gKiBTZWUgdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqL1xuXG5pbXBvcnQgY3JlYXRlTHVjaWRlSWNvbiBmcm9tICcuLi9jcmVhdGVMdWNpZGVJY29uLmpzJztcblxuY29uc3QgUmVmcmVzaEN3ID0gY3JlYXRlTHVjaWRlSWNvbihcIlJlZnJlc2hDd1wiLCBbXG4gIFtcInBhdGhcIiwgeyBkOiBcIk0zIDEyYTkgOSAwIDAgMSA5LTkgOS43NSA5Ljc1IDAgMCAxIDYuNzQgMi43NEwyMSA4XCIsIGtleTogXCJ2OWg1dmNcIiB9XSxcbiAgW1wicGF0aFwiLCB7IGQ6IFwiTTIxIDN2NWgtNVwiLCBrZXk6IFwiMXE3dG8wXCIgfV0sXG4gIFtcInBhdGhcIiwgeyBkOiBcIk0yMSAxMmE5IDkgMCAwIDEtOSA5IDkuNzUgOS43NSAwIDAgMS02Ljc0LTIuNzRMMyAxNlwiLCBrZXk6IFwiM3VpZmwzXCIgfV0sXG4gIFtcInBhdGhcIiwgeyBkOiBcIk04IDE2SDN2NVwiLCBrZXk6IFwiMWN2Njc4XCIgfV1cbl0pO1xuXG5leHBvcnQgeyBSZWZyZXNoQ3cgYXMgZGVmYXVsdCB9O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9cmVmcmVzaC1jdy5qcy5tYXBcbiJdLCJuYW1lcyI6WyJjcmVhdGVMdWNpZGVJY29uIiwiUmVmcmVzaEN3IiwiZCIsImtleSIsImRlZmF1bHQiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(ssr)/./node_modules/lucide-react/dist/esm/icons/refresh-cw.js\n");
/***/ }),
/***/ "(ssr)/./node_modules/lucide-react/dist/esm/icons/search.js":
/*!************************************************************!*\
!*** ./node_modules/lucide-react/dist/esm/icons/search.js ***!

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -47,6 +47,11 @@
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/amd options */
/******/ (() => {
/******/ __webpack_require__.amdO = {};
/******/ })();
/******/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
@@ -125,7 +130,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("b2fa540f7745b378")
/******/ __webpack_require__.h = () => ("55eccca8b797fc49")
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -192,7 +192,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "4f3d48d519328cf2"; }
/******/ __webpack_require__.h = function() { return "8f42e63b535dc477"; }
/******/ }();
/******/
/******/ /* webpack/runtime/global */

View File

@@ -619,12 +619,21 @@ video {
.left-2\/3 {
left: 66.666667%;
}
.bottom-6 {
bottom: 1.5rem;
}
.right-6 {
right: 1.5rem;
}
.z-10 {
z-index: 10;
}
.z-\[9999\] {
z-index: 9999;
}
.z-50 {
z-index: 50;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
@@ -728,6 +737,9 @@ video {
.h-1\.5 {
height: 0.375rem;
}
.h-6 {
height: 1.5rem;
}
.min-h-screen {
min-height: 100vh;
}
@@ -752,6 +764,12 @@ video {
.w-px {
width: 1px;
}
.w-6 {
width: 1.5rem;
}
.min-w-\[280px\] {
min-width: 280px;
}
.max-w-5xl {
max-width: 64rem;
}
@@ -767,6 +785,9 @@ video {
.max-w-4xl {
max-width: 56rem;
}
.max-w-\[120px\] {
max-width: 120px;
}
.flex-1 {
flex: 1 1 0%;
}
@@ -951,6 +972,9 @@ video {
.border-t {
border-top-width: 1px;
}
.border-b {
border-bottom-width: 1px;
}
.border-dashed {
border-style: dashed;
}
@@ -1029,6 +1053,10 @@ video {
.border-amber-200\/50 {
border-color: rgb(253 230 138 / 0.5);
}
.border-purple-100 {
--tw-border-opacity: 1;
border-color: rgb(243 232 255 / var(--tw-border-opacity, 1));
}
.bg-amber-600 {
--tw-bg-opacity: 1;
background-color: rgb(217 119 6 / var(--tw-bg-opacity, 1));
@@ -1129,6 +1157,18 @@ video {
--tw-bg-opacity: 1;
background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1));
}
.bg-purple-50 {
--tw-bg-opacity: 1;
background-color: rgb(250 245 255 / var(--tw-bg-opacity, 1));
}
.bg-zinc-800 {
--tw-bg-opacity: 1;
background-color: rgb(39 39 42 / var(--tw-bg-opacity, 1));
}
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1));
}
.bg-gradient-to-t {
background-image: linear-gradient(to top, var(--tw-gradient-stops));
}
@@ -1228,6 +1268,10 @@ video {
padding-left: 0.625rem;
padding-right: 0.625rem;
}
.px-1\.5 {
padding-left: 0.375rem;
padding-right: 0.375rem;
}
.pl-10 {
padding-left: 2.5rem;
}
@@ -1249,6 +1293,9 @@ video {
.pb-3 {
padding-bottom: 0.75rem;
}
.pt-1 {
padding-top: 0.25rem;
}
.text-center {
text-align: center;
}
@@ -1296,6 +1343,9 @@ video {
.text-\[9px\] {
font-size: 9px;
}
.text-\[8px\] {
font-size: 8px;
}
.font-bold {
font-weight: 700;
}
@@ -1417,6 +1467,10 @@ video {
--tw-text-opacity: 1;
color: rgb(212 212 216 / var(--tw-text-opacity, 1));
}
.text-purple-500 {
--tw-text-opacity: 1;
color: rgb(168 85 247 / var(--tw-text-opacity, 1));
}
.accent-amber-600 {
accent-color: #d97706;
}
@@ -1426,6 +1480,9 @@ video {
.opacity-70 {
opacity: 0.7;
}
.opacity-50 {
opacity: 0.5;
}
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
@@ -1817,6 +1874,10 @@ body {
border-color: rgb(63 63 70 / 0.5);
}
.dark\:border-purple-800\/30 {
border-color: rgb(107 33 168 / 0.3);
}
.dark\:bg-green-900\/10 {
background-color: rgb(20 83 45 / 0.1);
}
@@ -1895,6 +1956,10 @@ body {
background-color: rgb(120 53 15 / 0.2);
}
.dark\:bg-purple-900\/10 {
background-color: rgb(88 28 135 / 0.1);
}
.dark\:text-zinc-100 {
--tw-text-opacity: 1;
color: rgb(244 244 245 / var(--tw-text-opacity, 1));

View File

@@ -0,0 +1 @@
{"c":["app/page","app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1 @@
{"c":["app/page","app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1 @@
{"c":["app/page","app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"f9a2a6d45f8c\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImY5YTJhNmQ0NWY4Y1wiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"34a6b6a27bf9\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcIjM0YTZiNmEyN2JmOVwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"d4e313d6ea8f\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImQ0ZTMxM2Q2ZWE4ZlwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,32 @@
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&server=false!":
/*!*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
!*** ./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&server=false! ***!
\*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
eval(__webpack_require__.ts("Promise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/app/globals.css */ \"(app-pages-browser)/./src/app/globals.css\"));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.t.bind(__webpack_require__, /*! ./node_modules/next/font/google/target.css?{\"path\":\"src/app/layout.tsx\",\"import\":\"Inter\",\"arguments\":[{\"subsets\":[\"latin\"]}],\"variableName\":\"inter\"} */ \"(app-pages-browser)/./node_modules/next/font/google/target.css?{\\\"path\\\":\\\"src/app/layout.tsx\\\",\\\"import\\\":\\\"Inter\\\",\\\"arguments\\\":[{\\\"subsets\\\":[\\\"latin\\\"]}],\\\"variableName\\\":\\\"inter\\\"}\", 23));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/components/OfflineIndicator.tsx */ \"(app-pages-browser)/./src/components/OfflineIndicator.tsx\"));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/components/PWARegistration.tsx */ \"(app-pages-browser)/./src/components/PWARegistration.tsx\"))//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL25vZGVfbW9kdWxlcy9uZXh0L2Rpc3QvYnVpbGQvd2VicGFjay9sb2FkZXJzL25leHQtZmxpZ2h0LWNsaWVudC1lbnRyeS1sb2FkZXIuanM/bW9kdWxlcz0lMkZob21lJTJGcm9iaW4lMkZBSSUyRkNvZGluZyUyRldoaXNreSUyRnNyYyUyRmFwcCUyRmdsb2JhbHMuY3NzJm1vZHVsZXM9JTJGaG9tZSUyRnJvYmluJTJGQUklMkZDb2RpbmclMkZXaGlza3klMkZub2RlX21vZHVsZXMlMkZuZXh0JTJGZm9udCUyRmdvb2dsZSUyRnRhcmdldC5jc3MlM0YlN0IlMjJwYXRoJTIyJTNBJTIyc3JjJTJGYXBwJTJGbGF5b3V0LnRzeCUyMiUyQyUyMmltcG9ydCUyMiUzQSUyMkludGVyJTIyJTJDJTIyYXJndW1lbnRzJTIyJTNBJTVCJTdCJTIyc3Vic2V0cyUyMiUzQSU1QiUyMmxhdGluJTIyJTVEJTdEJTVEJTJDJTIydmFyaWFibGVOYW1lJTIyJTNBJTIyaW50ZXIlMjIlN0QmbW9kdWxlcz0lMkZob21lJTJGcm9iaW4lMkZBSSUyRkNvZGluZyUyRldoaXNreSUyRnNyYyUyRmNvbXBvbmVudHMlMkZPZmZsaW5lSW5kaWNhdG9yLnRzeCZtb2R1bGVzPSUyRmhvbWUlMkZyb2JpbiUyRkFJJTJGQ29kaW5nJTJGV2hpc2t5JTJGc3JjJTJGY29tcG9uZW50cyUyRlBXQVJlZ2lzdHJhdGlvbi50c3gmc2VydmVyPWZhbHNlISIsIm1hcHBpbmdzIjoiQUFBQSxvS0FBcUY7QUFDckYsMGJBQXNPO0FBQ3RPLG9NQUFxRztBQUNyRyIsInNvdXJjZXMiOlsid2VicGFjazovL19OX0UvPzgxMWYiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0KC8qIHdlYnBhY2tNb2RlOiBcImVhZ2VyXCIgKi8gXCIvaG9tZS9yb2Jpbi9BSS9Db2RpbmcvV2hpc2t5L3NyYy9hcHAvZ2xvYmFscy5jc3NcIik7XG5pbXBvcnQoLyogd2VicGFja01vZGU6IFwiZWFnZXJcIiAqLyBcIi9ob21lL3JvYmluL0FJL0NvZGluZy9XaGlza3kvbm9kZV9tb2R1bGVzL25leHQvZm9udC9nb29nbGUvdGFyZ2V0LmNzcz97XFxcInBhdGhcXFwiOlxcXCJzcmMvYXBwL2xheW91dC50c3hcXFwiLFxcXCJpbXBvcnRcXFwiOlxcXCJJbnRlclxcXCIsXFxcImFyZ3VtZW50c1xcXCI6W3tcXFwic3Vic2V0c1xcXCI6W1xcXCJsYXRpblxcXCJdfV0sXFxcInZhcmlhYmxlTmFtZVxcXCI6XFxcImludGVyXFxcIn1cIik7XG5pbXBvcnQoLyogd2VicGFja01vZGU6IFwiZWFnZXJcIiAqLyBcIi9ob21lL3JvYmluL0FJL0NvZGluZy9XaGlza3kvc3JjL2NvbXBvbmVudHMvT2ZmbGluZUluZGljYXRvci50c3hcIik7XG5pbXBvcnQoLyogd2VicGFja01vZGU6IFwiZWFnZXJcIiAqLyBcIi9ob21lL3JvYmluL0FJL0NvZGluZy9XaGlza3kvc3JjL2NvbXBvbmVudHMvUFdBUmVnaXN0cmF0aW9uLnRzeFwiKSJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&server=false!\n"));
/***/ }),
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"e47fd9d95190\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImU0N2ZkOWQ5NTE5MFwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"d06fa64eba9c\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImQwNmZhNjRlYmE5Y1wiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"ae5cca688a4f\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImFlNWNjYTY4OGE0ZlwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

View File

@@ -0,0 +1,21 @@
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FUploadQueue.tsx&server=false!":
/*!*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\
!*** ./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FUploadQueue.tsx&server=false! ***!
\*******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
eval(__webpack_require__.ts("Promise.resolve(/*! import() eager */).then(__webpack_require__.t.bind(__webpack_require__, /*! ./node_modules/next/font/google/target.css?{\"path\":\"src/app/layout.tsx\",\"import\":\"Inter\",\"arguments\":[{\"subsets\":[\"latin\"]}],\"variableName\":\"inter\"} */ \"(app-pages-browser)/./node_modules/next/font/google/target.css?{\\\"path\\\":\\\"src/app/layout.tsx\\\",\\\"import\\\":\\\"Inter\\\",\\\"arguments\\\":[{\\\"subsets\\\":[\\\"latin\\\"]}],\\\"variableName\\\":\\\"inter\\\"}\", 23));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/app/globals.css */ \"(app-pages-browser)/./src/app/globals.css\"));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/components/OfflineIndicator.tsx */ \"(app-pages-browser)/./src/components/OfflineIndicator.tsx\"));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/components/PWARegistration.tsx */ \"(app-pages-browser)/./src/components/PWARegistration.tsx\"));\nPromise.resolve(/*! import() eager */).then(__webpack_require__.bind(__webpack_require__, /*! ./src/components/UploadQueue.tsx */ \"(app-pages-browser)/./src/components/UploadQueue.tsx\"))//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL25vZGVfbW9kdWxlcy9uZXh0L2Rpc3QvYnVpbGQvd2VicGFjay9sb2FkZXJzL25leHQtZmxpZ2h0LWNsaWVudC1lbnRyeS1sb2FkZXIuanM/bW9kdWxlcz0lMkZob21lJTJGcm9iaW4lMkZBSSUyRkNvZGluZyUyRldoaXNreSUyRm5vZGVfbW9kdWxlcyUyRm5leHQlMkZmb250JTJGZ29vZ2xlJTJGdGFyZ2V0LmNzcyUzRiU3QiUyMnBhdGglMjIlM0ElMjJzcmMlMkZhcHAlMkZsYXlvdXQudHN4JTIyJTJDJTIyaW1wb3J0JTIyJTNBJTIySW50ZXIlMjIlMkMlMjJhcmd1bWVudHMlMjIlM0ElNUIlN0IlMjJzdWJzZXRzJTIyJTNBJTVCJTIybGF0aW4lMjIlNUQlN0QlNUQlMkMlMjJ2YXJpYWJsZU5hbWUlMjIlM0ElMjJpbnRlciUyMiU3RCZtb2R1bGVzPSUyRmhvbWUlMkZyb2JpbiUyRkFJJTJGQ29kaW5nJTJGV2hpc2t5JTJGc3JjJTJGYXBwJTJGZ2xvYmFscy5jc3MmbW9kdWxlcz0lMkZob21lJTJGcm9iaW4lMkZBSSUyRkNvZGluZyUyRldoaXNreSUyRnNyYyUyRmNvbXBvbmVudHMlMkZPZmZsaW5lSW5kaWNhdG9yLnRzeCZtb2R1bGVzPSUyRmhvbWUlMkZyb2JpbiUyRkFJJTJGQ29kaW5nJTJGV2hpc2t5JTJGc3JjJTJGY29tcG9uZW50cyUyRlBXQVJlZ2lzdHJhdGlvbi50c3gmbW9kdWxlcz0lMkZob21lJTJGcm9iaW4lMkZBSSUyRkNvZGluZyUyRldoaXNreSUyRnNyYyUyRmNvbXBvbmVudHMlMkZVcGxvYWRRdWV1ZS50c3gmc2VydmVyPWZhbHNlISIsIm1hcHBpbmdzIjoiQUFBQSwwYkFBc087QUFDdE8sb0tBQXFGO0FBQ3JGLG9NQUFxRztBQUNyRyxrTUFBb0c7QUFDcEciLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLz83MzM0Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCgvKiB3ZWJwYWNrTW9kZTogXCJlYWdlclwiICovIFwiL2hvbWUvcm9iaW4vQUkvQ29kaW5nL1doaXNreS9ub2RlX21vZHVsZXMvbmV4dC9mb250L2dvb2dsZS90YXJnZXQuY3NzP3tcXFwicGF0aFxcXCI6XFxcInNyYy9hcHAvbGF5b3V0LnRzeFxcXCIsXFxcImltcG9ydFxcXCI6XFxcIkludGVyXFxcIixcXFwiYXJndW1lbnRzXFxcIjpbe1xcXCJzdWJzZXRzXFxcIjpbXFxcImxhdGluXFxcIl19XSxcXFwidmFyaWFibGVOYW1lXFxcIjpcXFwiaW50ZXJcXFwifVwiKTtcbmltcG9ydCgvKiB3ZWJwYWNrTW9kZTogXCJlYWdlclwiICovIFwiL2hvbWUvcm9iaW4vQUkvQ29kaW5nL1doaXNreS9zcmMvYXBwL2dsb2JhbHMuY3NzXCIpO1xuaW1wb3J0KC8qIHdlYnBhY2tNb2RlOiBcImVhZ2VyXCIgKi8gXCIvaG9tZS9yb2Jpbi9BSS9Db2RpbmcvV2hpc2t5L3NyYy9jb21wb25lbnRzL09mZmxpbmVJbmRpY2F0b3IudHN4XCIpO1xuaW1wb3J0KC8qIHdlYnBhY2tNb2RlOiBcImVhZ2VyXCIgKi8gXCIvaG9tZS9yb2Jpbi9BSS9Db2RpbmcvV2hpc2t5L3NyYy9jb21wb25lbnRzL1BXQVJlZ2lzdHJhdGlvbi50c3hcIik7XG5pbXBvcnQoLyogd2VicGFja01vZGU6IFwiZWFnZXJcIiAqLyBcIi9ob21lL3JvYmluL0FJL0NvZGluZy9XaGlza3kvc3JjL2NvbXBvbmVudHMvVXBsb2FkUXVldWUudHN4XCIpIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FUploadQueue.tsx&server=false!\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("app/layout",{
/***/ "(app-pages-browser)/./src/app/globals.css":
/*!*****************************!*\
!*** ./src/app/globals.css ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"b653bf8a7b0f\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL3NyYy9hcHAvZ2xvYmFscy5jc3MiLCJtYXBwaW5ncyI6IjtBQUFBLCtEQUFlLGNBQWM7QUFDN0IsSUFBSSxJQUFVLElBQUksaUJBQWlCIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vX05fRS8uL3NyYy9hcHAvZ2xvYmFscy5jc3M/ZWY4NSJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZGVmYXVsdCBcImI2NTNiZjhhN2IwZlwiXG5pZiAobW9kdWxlLmhvdCkgeyBtb2R1bGUuaG90LmFjY2VwdCgpIH1cbiJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(app-pages-browser)/./src/app/globals.css\n"));
/***/ })
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":["(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&server=false!"]}

View File

@@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":["(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FUploadQueue.tsx&server=false!"]}

View File

@@ -0,0 +1 @@
{"c":["app/layout","webpack"],"r":[],"m":["(app-pages-browser)/./node_modules/next/dist/build/webpack/loaders/next-flight-client-entry-loader.js?modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fapp%2Fglobals.css&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fnode_modules%2Fnext%2Ffont%2Fgoogle%2Ftarget.css%3F%7B%22path%22%3A%22src%2Fapp%2Flayout.tsx%22%2C%22import%22%3A%22Inter%22%2C%22arguments%22%3A%5B%7B%22subsets%22%3A%5B%22latin%22%5D%7D%5D%2C%22variableName%22%3A%22inter%22%7D&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FOfflineIndicator.tsx&modules=%2Fhome%2Frobin%2FAI%2FCoding%2FWhisky%2Fsrc%2Fcomponents%2FPWARegistration.tsx&server=false!"]}

View File

@@ -0,0 +1 @@
{"c":["app/page","app/layout","webpack"],"r":[],"m":[]}

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "b29af26d43c02a06"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "2b916cde7b7b9504"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "f1d7d2bffa057db4"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "eed8b8814d6f064d"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "b72da91da115626a"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "b020820827bb4763"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "a653393fa0b83b7c"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "c49db8bb8c302319"; }
/******/ }();
/******/
/******/ }
);

View File

@@ -0,0 +1,18 @@
"use strict";
/*
* ATTENTION: An "eval-source-map" devtool has been used.
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
self["webpackHotUpdate_N_E"]("webpack",{},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "8f42e63b535dc477"; }
/******/ }();
/******/
/******/ }
);

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
import "./globals.css";
import PWARegistration from "@/components/PWARegistration";
import OfflineIndicator from "@/components/OfflineIndicator";
import UploadQueue from "@/components/UploadQueue";
const inter = Inter({ subsets: ["latin"] });
@@ -41,6 +42,7 @@ export default function RootLayout({
<body className={inter.className}>
<PWARegistration />
<OfflineIndicator />
<UploadQueue />
{children}
</body>
</html>

View File

@@ -2,7 +2,7 @@
import React, { useState, useMemo } from 'react';
import Link from 'next/link';
import { Search, Filter, X, Calendar, Clock, Package, Lock, Unlock, Ghost, FlaskConical } from 'lucide-react';
import { Search, Filter, X, Calendar, Clock, Package, Lock, Unlock, Ghost, FlaskConical, AlertCircle } from 'lucide-react';
interface BottleCardProps {
bottle: any;
@@ -45,9 +45,18 @@ function BottleCard({ bottle }: BottleCardProps) {
<div className="p-5 space-y-4">
<div>
<p className="text-[10px] font-black text-amber-600 uppercase tracking-[0.2em] mb-1 leading-none">{bottle.distillery}</p>
<h3 className="font-black text-xl text-zinc-900 dark:text-zinc-100 leading-tight group-hover:text-amber-600 transition-colors line-clamp-2 min-h-[3.5rem] flex items-center">
{bottle.name}
<div className="flex justify-between items-start mb-1">
<p className="text-[10px] font-black text-amber-600 uppercase tracking-[0.2em] leading-none">{bottle.distillery}</p>
{(bottle.is_whisky === false || (bottle.confidence && bottle.confidence < 70)) && (
<div className="flex items-center gap-1 text-[8px] font-black bg-red-500 text-white px-1.5 py-0.5 rounded-full animate-pulse">
<AlertCircle size={8} />
REVIEW
</div>
)}
</div>
<h3 className={`font-black text-xl leading-tight group-hover:text-amber-600 transition-colors line-clamp-2 min-h-[3.5rem] flex items-center ${bottle.is_whisky === false ? 'text-red-600 dark:text-red-400' : 'text-zinc-900 dark:text-zinc-100'
}`}>
{bottle.name || 'Unbekannte Flasche'}
</h3>
</div>

View File

@@ -6,6 +6,8 @@ import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { analyzeBottle } from '@/services/analyze-bottle';
import { saveBottle } from '@/services/save-bottle';
import { BottleMetadata } from '@/types/whisky';
import { savePendingBottle } from '@/lib/offline-db';
import { v4 as uuidv4 } from 'uuid';
interface CameraCaptureProps {
onImageCaptured?: (base64Image: string) => void;
@@ -21,6 +23,7 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [analysisResult, setAnalysisResult] = useState<BottleMetadata | null>(null);
const [isQueued, setIsQueued] = useState(false);
const handleCapture = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
@@ -29,6 +32,7 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
setIsProcessing(true);
setError(null);
setAnalysisResult(null);
setIsQueued(false);
try {
const compressedBase64 = await compressImage(file);
@@ -38,6 +42,18 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
onImageCaptured(compressedBase64);
}
// Check if Offline
if (!navigator.onLine) {
console.log('Offline detected. Queuing image...');
await savePendingBottle({
id: uuidv4(),
imageBase64: compressedBase64,
timestamp: Date.now(),
});
setIsQueued(true);
return;
}
const response = await analyzeBottle(compressedBase64);
if (response.success && response.data) {
@@ -162,7 +178,7 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
/>
<button
onClick={previewUrl && analysisResult ? handleSave : triggerUpload}
onClick={isQueued ? () => setPreviewUrl(null) : (previewUrl && analysisResult ? handleSave : triggerUpload)}
disabled={isProcessing || isSaving}
className="w-full py-4 px-6 bg-amber-600 hover:bg-amber-700 text-white rounded-xl font-semibold flex items-center justify-center gap-2 transition-all active:scale-[0.98] shadow-lg shadow-amber-600/20 disabled:opacity-50"
>
@@ -171,6 +187,11 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-white"></div>
Wird gespeichert...
</>
) : isQueued ? (
<>
<CheckCircle2 size={20} />
Nächste Flasche
</>
) : previewUrl && analysisResult ? (
<>
<CheckCircle2 size={20} />
@@ -196,7 +217,14 @@ export default function CameraCapture({ onImageCaptured, onAnalysisComplete, onS
</div>
)}
{previewUrl && !isProcessing && !error && (
{isQueued && (
<div className="flex items-center gap-2 text-purple-500 text-sm bg-purple-50 dark:bg-purple-900/10 p-4 rounded-xl w-full border border-purple-100 dark:border-purple-800/30 font-medium">
<Sparkles size={16} />
Offline! Foto wurde gemerkt wird automatisch analysiert, sobald du wieder Netz hast. 📡
</div>
)}
{previewUrl && !isProcessing && !error && !isQueued && (
<div className="flex flex-col gap-3 w-full animate-in fade-in slide-in-from-top-4 duration-500">
<div className="flex items-center gap-2 text-green-500 text-sm bg-green-50 dark:bg-green-900/10 p-3 rounded-lg w-full">
<CheckCircle2 size={16} />

View File

@@ -0,0 +1,125 @@
'use client';
import React, { useEffect, useState, useCallback } from 'react';
import { getAllPendingBottles, deletePendingBottle, PendingBottle } from '@/lib/offline-db';
import { analyzeBottle } from '@/services/analyze-bottle';
import { saveBottle } from '@/services/save-bottle';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { RefreshCw, CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
export default function UploadQueue() {
const supabase = createClientComponentClient();
const [queue, setQueue] = useState<PendingBottle[]>([]);
const [isSyncing, setIsSyncing] = useState(false);
const [currentProgress, setCurrentProgress] = useState<{ id: string, status: string } | null>(null);
const loadQueue = useCallback(async () => {
const pending = await getAllPendingBottles();
setQueue(pending);
}, []);
const syncQueue = useCallback(async () => {
if (isSyncing || !navigator.onLine || queue.length === 0) return;
setIsSyncing(true);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
console.error('No user found for background sync');
setIsSyncing(false);
return;
}
for (const item of queue) {
setCurrentProgress({ id: item.id, status: 'Analysiere...' });
try {
// 1. Analyze
const analysis = await analyzeBottle(item.imageBase64);
if (analysis.success && analysis.data) {
setCurrentProgress({ id: item.id, status: 'Speichere...' });
// 2. Save
const save = await saveBottle(analysis.data, item.imageBase64, user.id);
if (save.success) {
await deletePendingBottle(item.id);
}
}
} catch (err) {
console.error('Sync failed for item', item.id, err);
}
}
setIsSyncing(false);
setCurrentProgress(null);
loadQueue();
}, [isSyncing, queue, supabase, loadQueue]);
useEffect(() => {
loadQueue();
// Listen for storage changes (e.g. from CameraCapture)
const interval = setInterval(loadQueue, 5000);
const handleOnline = () => {
console.log('Back online! Triggering sync...');
syncQueue();
};
window.addEventListener('online', handleOnline);
return () => {
clearInterval(interval);
window.removeEventListener('online', handleOnline);
};
}, [loadQueue, syncQueue]);
if (queue.length === 0) return null;
return (
<div className="fixed bottom-6 right-6 z-50 animate-in slide-in-from-right-10">
<div className="bg-zinc-900 text-white p-4 rounded-2xl shadow-2xl border border-white/10 flex flex-col gap-3 min-w-[280px]">
<div className="flex items-center justify-between border-b border-white/10 pb-2">
<div className="flex items-center gap-2">
<RefreshCw size={16} className={isSyncing ? 'animate-spin text-amber-500' : 'text-zinc-400'} />
<span className="text-xs font-black uppercase tracking-widest">Upload Queue</span>
</div>
<span className="bg-amber-600 text-[10px] font-black px-1.5 py-0.5 rounded-md">
{queue.length}
</span>
</div>
<div className="space-y-2">
{queue.slice(0, 3).map((item) => (
<div key={item.id} className="flex items-center justify-between text-[11px] font-medium text-zinc-400">
<div className="flex items-center gap-2">
<div className="w-6 h-6 rounded bg-zinc-800 overflow-hidden">
<img src={item.imageBase64} className="w-full h-full object-cover opacity-50" />
</div>
<span className="truncate max-w-[120px]">
{currentProgress?.id === item.id ? currentProgress.status : 'Wartet auf Netz...'}
</span>
</div>
{currentProgress?.id === item.id ? (
<Loader2 size={12} className="animate-spin text-amber-500" />
) : (
<AlertCircle size={12} className="text-zinc-600" />
)}
</div>
))}
{queue.length > 3 && (
<div className="text-[10px] text-zinc-500 text-center font-bold italic pt-1">
+ {queue.length - 3} weitere Flaschen
</div>
)}
</div>
{navigator.onLine && !isSyncing && (
<button
onClick={syncQueue}
className="w-full py-2 bg-amber-600 hover:bg-amber-500 text-[10px] font-black uppercase rounded-lg transition-colors cursor-pointer"
>
Jetzt Synchronisieren
</button>
)}
</div>
</div>
);
}

View File

@@ -12,9 +12,10 @@ export const geminiModel = genAI.getGenerativeModel({
});
export const SYSTEM_INSTRUCTION = `
You are a sommelier and database clerk. Analyze the whisky bottle image. Extract precise metadata.
If a value is not visible, use null.
Infer the 'Category' (e.g., Islay Single Malt) based on the Distillery if possible.
You are a sommelier and database clerk. Analyze the whisky bottle image. Extract precise metadata.
If the image is NOT a whisky bottle or if you are very unsure, set "is_whisky" to false and provide a low "confidence" score.
If a value is not visible, use null.
Infer the 'Category' (e.g., Islay Single Malt) based on the Distillery if possible.
Search specifically for a "Whiskybase ID" or "WB ID" on the label.
Output raw JSON matching the following schema:
{
@@ -25,6 +26,8 @@ Output raw JSON matching the following schema:
"age": number | null,
"vintage": string | null,
"bottleCode": string | null,
"whiskybaseId": string | null
"whiskybaseId": string | null,
"is_whisky": boolean,
"confidence": number (0-100)
}
`;

66
src/lib/offline-db.ts Normal file
View File

@@ -0,0 +1,66 @@
export interface PendingBottle {
id: string;
imageBase64: string;
timestamp: number;
}
const DB_NAME = 'WhiskyVaultOffline';
const STORE_NAME = 'pendingCaptures';
const DB_VERSION = 1;
export const openDB = (): Promise<IDBDatabase> => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
};
request.onsuccess = (event) => {
resolve((event.target as IDBOpenDBRequest).result);
};
request.onerror = (event) => {
reject((event.target as IDBOpenDBRequest).error);
};
});
};
export const savePendingBottle = async (bottle: PendingBottle): Promise<void> => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction(STORE_NAME, 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.put(bottle);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
};
export const getAllPendingBottles = async (): Promise<PendingBottle[]> => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction(STORE_NAME, 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};
export const deletePendingBottle = async (id: string): Promise<void> => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction(STORE_NAME, 'readwrite');
const store = transaction.objectStore(STORE_NAME);
const request = store.delete(id);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
};

View File

@@ -53,6 +53,8 @@ export async function saveBottle(
whiskybase_id: metadata.whiskybaseId,
image_url: publicUrl,
status: 'sealed', // Default status
is_whisky: metadata.is_whisky ?? true,
confidence: metadata.confidence ?? 100,
})
.select()
.single();

View File

@@ -9,6 +9,8 @@ export const BottleMetadataSchema = z.object({
vintage: z.string().nullable(),
bottleCode: z.string().nullable(),
whiskybaseId: z.string().nullable(),
is_whisky: z.boolean().default(true),
confidence: z.number().min(0).max(100).default(100),
});
export type BottleMetadata = z.infer<typeof BottleMetadataSchema>;

View File

@@ -40,6 +40,8 @@ CREATE TABLE IF NOT EXISTS bottles (
status TEXT DEFAULT 'sealed' CHECK (status IN ('sealed', 'open', 'empty')),
whiskybase_id TEXT,
image_url TEXT,
is_whisky BOOLEAN DEFAULT true,
confidence INTEGER DEFAULT 100,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
);