Compare commits

..
4 Commits
8 changed files with 566 additions and 336 deletions

No files matched your search

+216 -250
View File
@@ -25,6 +25,21 @@
"pkg": "^5.8.1" "pkg": "^5.8.1"
} }
}, },
"node_modules/@babel/generator": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz",
"integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.18.2",
"@jridgewell/gen-mapping": "^0.3.0",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
@@ -45,6 +60,34 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/parser": {
"version": "7.18.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz",
"integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==",
"dev": true,
"license": "MIT",
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz",
"integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.18.10",
"@babel/helper-validator-identifier": "^7.18.6",
"to-fast-properties": "^2.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -123,9 +166,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "26.1.0", "version": "26.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.3.0.tgz",
"integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", "integrity": "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==",
"license": "MIT", "license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
@@ -274,9 +317,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/better-sqlite3": { "node_modules/better-sqlite3": {
"version": "11.3.0", "version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
"integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -304,6 +347,20 @@
"readable-stream": "^3.4.0" "readable-stream": "^3.4.0"
} }
}, },
"node_modules/bl/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.6", "version": "1.20.6",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
@@ -585,15 +642,6 @@
"node": ">=4.0.0" "node": ">=4.0.0"
} }
}, },
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -904,45 +952,28 @@
"readable-stream": "^2.0.0" "readable-stream": "^2.0.0"
} }
}, },
"node_modules/from2/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/from2/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT"
},
"node_modules/from2/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/fs-constants": { "node_modules/fs-constants": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -1224,6 +1255,23 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/into-stream": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz",
"integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"from2": "^2.3.0",
"p-is-promise": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -1234,16 +1282,13 @@
} }
}, },
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.16.2", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"hasown": "^2.0.3" "has": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -1305,6 +1350,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/jsonfile": { "node_modules/jsonfile": {
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
@@ -1399,19 +1457,6 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime": { "node_modules/mime": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -1503,20 +1548,34 @@
"readable-stream": "^3.6.0" "readable-stream": "^3.6.0"
} }
}, },
"node_modules/multistream/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/mysql2": { "node_modules/mysql2": {
"version": "3.22.5", "version": "3.24.2",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.5.tgz", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.24.2.tgz",
"integrity": "sha512-95uZ2TrPWAZdwpB3vvvDbmEMcNG8yIeNCyu6GUcr/QnWEE/wXm7+mhOCsdQfWQDTV7qYT/PDUZ4U4UPP4AsXqQ==", "integrity": "sha512-l9kXeKGwd6VCbSjmpO/bLWb+YCYpbvE4whte8ec6InXebvCNyEEydyRCnRU+TSHNqRLJ8B+Tk1uWb+vMCJgJzA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"aws-ssl-profiles": "^1.1.2", "aws-ssl-profiles": "^1.1.2",
"denque": "^2.1.0",
"generate-function": "^2.3.1", "generate-function": "^2.3.1",
"iconv-lite": "^0.7.2", "iconv-lite": "^0.7.3",
"long": "^5.3.2", "long": "^5.3.2",
"lru.min": "^1.1.4", "lru.min": "^1.1.4",
"named-placeholders": "^1.1.6", "named-placeholders": "^1.1.6",
"sql-escaper": "^1.3.3" "sql-escaper": "^1.5.1"
}, },
"engines": { "engines": {
"node": ">= 8.0" "node": ">= 8.0"
@@ -1569,9 +1628,9 @@
} }
}, },
"node_modules/node-abi": { "node_modules/node-abi": {
"version": "3.94.0", "version": "3.95.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.95.0.tgz",
"integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", "integrity": "sha512-T9iGctuocf0qIWFFOTxPzjT5q0SILqaBYXt272tlBHvTKC5+3JnkMirLxNJNkXHtFyBjU2Jx+NL4Zipr0B/c6Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"semver": "^7.3.5" "semver": "^7.3.5"
@@ -1602,9 +1661,9 @@
} }
}, },
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "9.0.3", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.3.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.5.tgz",
"integrity": "sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==", "integrity": "sha512-wvjiKvjczmsN7U/8006JOdXubgBk2XFAbioDMbT+sM7cPs0QrhJTa6KBRX7P5REGGkDcLUz/EarWidb8G8C1jQ==",
"license": "MIT-0", "license": "MIT-0",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@@ -1695,14 +1754,14 @@
} }
}, },
"node_modules/pg": { "node_modules/pg": {
"version": "8.22.0", "version": "8.23.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz",
"integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"pg-connection-string": "^2.14.0", "pg-connection-string": "^2.14.0",
"pg-pool": "^3.14.0", "pg-pool": "^3.14.0",
"pg-protocol": "^1.15.0", "pg-protocol": "^1.16.0",
"pg-types": "2.2.0", "pg-types": "2.2.0",
"pgpass": "1.0.5" "pgpass": "1.0.5"
}, },
@@ -1753,9 +1812,9 @@
} }
}, },
"node_modules/pg-protocol": { "node_modules/pg-protocol": {
"version": "1.15.0", "version": "1.16.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz",
"integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/pg-types": { "node_modules/pg-types": {
@@ -1783,6 +1842,19 @@
"split2": "^4.1.0" "split2": "^4.1.0"
} }
}, },
"node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg": { "node_modules/pkg": {
"version": "5.8.1", "version": "5.8.1",
"resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz", "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.8.1.tgz",
@@ -1837,124 +1909,6 @@
"pkg-fetch": "lib-es5/bin.js" "pkg-fetch": "lib-es5/bin.js"
} }
}, },
"node_modules/pkg-fetch/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/pkg/node_modules/@babel/generator": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz",
"integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.18.2",
"@jridgewell/gen-mapping": "^0.3.0",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/pkg/node_modules/@babel/parser": {
"version": "7.18.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz",
"integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==",
"dev": true,
"license": "MIT",
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/pkg/node_modules/@babel/types": {
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz",
"integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.18.10",
"@babel/helper-validator-identifier": "^7.18.6",
"to-fast-properties": "^2.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/pkg/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/pkg/node_modules/into-stream": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz",
"integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==",
"dev": true,
"license": "MIT",
"dependencies": {
"from2": "^2.3.0",
"p-is-promise": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pkg/node_modules/is-core-module": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/pkg/node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pkg/node_modules/napi-build-utils": { "node_modules/pkg/node_modules/napi-build-utils": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
@@ -2173,19 +2127,28 @@
} }
}, },
"node_modules/readable-stream": { "node_modules/readable-stream": {
"version": "3.6.2", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"inherits": "^2.0.3", "core-util-is": "~1.0.0",
"string_decoder": "^1.1.1", "inherits": "~2.0.3",
"util-deprecate": "^1.0.1" "isarray": "~1.0.0",
}, "process-nextick-args": "~2.0.0",
"engines": { "safe-buffer": "~5.1.1",
"node": ">= 6" "string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
} }
}, },
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT"
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -2218,6 +2181,22 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/resolve/node_modules/is-core-module": {
"version": "2.16.2",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
"integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reusify": { "node_modules/reusify": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -2479,9 +2458,9 @@
} }
}, },
"node_modules/sql-escaper": { "node_modules/sql-escaper": {
"version": "1.3.3", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz", "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.5.1.tgz",
"integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", "integrity": "sha512-4toX5E1fQbBrpfXidaHnF0669nkAdETeIPTs2SUjxxD7RRIs9ICG4gtpmfc68JCEKehsdwLFqBu9VlQqZ1P1gg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"bun": ">=1.0.0", "bun": ">=1.0.0",
@@ -2524,47 +2503,20 @@
"readable-stream": "^2.1.4" "readable-stream": "^2.1.4"
} }
}, },
"node_modules/stream-meter/node_modules/readable-stream": { "node_modules/string_decoder": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/stream-meter/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT"
},
"node_modules/stream-meter/node_modules/string_decoder": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
}, },
"node_modules/string_decoder": { "node_modules/string_decoder/node_modules/safe-buffer": {
"version": "1.3.0", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT", "license": "MIT"
"dependencies": {
"safe-buffer": "~5.2.0"
}
}, },
"node_modules/string-width": { "node_modules/string-width": {
"version": "4.2.3", "version": "4.2.3",
@@ -2657,6 +2609,20 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/tar-stream/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+2 -7
View File
@@ -197,13 +197,8 @@
<input type="text" class="search-input" id="global-search" <input type="text" class="search-input" id="global-search"
placeholder="Cerca ticket (numero, titolo o corpo)..." style="padding-right: 32px;" /> placeholder="Cerca ticket (numero, titolo o corpo)..." style="padding-right: 32px;" />
<button id="global-search-clear" <button id="global-search-clear"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: none; border: none; padding: 4px; cursor: pointer; color: var(--text-muted); display: none; align-items: center; justify-content: center;" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: none; border: none; padding: 0; cursor: pointer; color: var(--text-muted); display: none; align-items: center; justify-content: center; font-size: 13px; line-height: 1;"
title="Cancella ricerca"> title="Cancella ricerca">✕</button>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
style="width:14px;height:14px;display:block;">
<path d="M18 6L6 18M6 6l12 12" />
</svg>
</button>
</div> </div>
<button class="btn btn-primary btn-sm" id="topbar-new-ticket" onclick="window.location.hash='#/tickets/new'"> <button class="btn btn-primary btn-sm" id="topbar-new-ticket" onclick="window.location.hash='#/tickets/new'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;">
+34 -12
View File
@@ -163,12 +163,20 @@ const App = {
searchInput.addEventListener('keydown', (e) => { searchInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
const hash = window.location.hash; const query = searchInput.value.trim();
if (!hash.startsWith('#/tickets') || hash.includes('/new') || hash.match(/#\/tickets\/\d+/)) { searchInput.value = '';
window.location.hash = '#/tickets'; toggleClearBtn();
if (query) {
const newHash = `#/ricerca-spudorata?q=${encodeURIComponent(query)}`;
if (window.location.hash === newHash && typeof RicercaSpudorataView !== 'undefined') {
const input = document.getElementById('spudorata-search-input');
if (input) input.value = query;
RicercaSpudorataView.executeSearch(query);
} else { } else {
TicketListView.currentPage = 1; window.location.hash = newHash;
TicketListView.render(); }
} else {
window.location.hash = '#/ricerca-spudorata';
} }
} }
}); });
@@ -178,12 +186,9 @@ const App = {
searchInput.value = ''; searchInput.value = '';
searchClearBtn.style.display = 'none'; searchClearBtn.style.display = 'none';
searchInput.focus(); searchInput.focus();
const hash = window.location.hash; if (window.location.hash.startsWith('#/ricerca-spudorata') && typeof RicercaSpudorataView !== 'undefined') {
if (hash.startsWith('#/tickets') && !hash.includes('/new') && !hash.match(/#\/tickets\/\d+/)) { RicercaSpudorataView.invalidateCache();
TicketListView.currentPage = 1; RicercaSpudorataView.render();
TicketListView.render();
} else {
window.location.hash = '#/tickets';
} }
}); });
} }
@@ -241,10 +246,16 @@ const App = {
} else { } else {
this.route(); this.route();
} }
// Set periodic refresh for daily time accounting counter (every 60 seconds)
if (!this.dailyTimerInterval) {
this.dailyTimerInterval = setInterval(() => this.updateDailyTimer(), 60000);
}
}, },
/** Route based on current hash */ /** Route based on current hash */
route() { route() {
this.updateDailyTimer();
const fullHash = window.location.hash || '#/dashboard'; const fullHash = window.location.hash || '#/dashboard';
const hash = fullHash.split('?')[0]; const hash = fullHash.split('?')[0];
const titleEl = document.getElementById('page-title'); const titleEl = document.getElementById('page-title');
@@ -364,7 +375,18 @@ const App = {
throw new Error(errData.error || errData.message || `HTTP ${response.status}`); throw new Error(errData.error || errData.message || `HTTP ${response.status}`);
} }
return response.json(); const data = await response.json();
// Automatically update daily timer on any system mutation (POST, PUT, PATCH, DELETE)
const method = (options.method || 'GET').toUpperCase();
if (method !== 'GET' && !url.includes('/api/users/time-today')) {
setTimeout(() => {
this.updateDailyTimer();
this.updateSidebarBadges();
}, 100);
}
return data;
}, },
/** Ensure lookup data is loaded (cached) */ /** Ensure lookup data is loaded (cached) */
+47 -1
View File
@@ -14,6 +14,7 @@ const Filters = {
customer_user_id: '', customer_user_id: '',
date_from: '', date_from: '',
date_to: '', date_to: '',
sub_search: '',
}, },
my: { my: {
queue_id: '', queue_id: '',
@@ -23,6 +24,7 @@ const Filters = {
customer_user_id: '', customer_user_id: '',
date_from: '', date_from: '',
date_to: '', date_to: '',
sub_search: '',
}, },
spudorata: { spudorata: {
queue_id: '', queue_id: '',
@@ -32,6 +34,7 @@ const Filters = {
customer_user_id: '', customer_user_id: '',
date_from: '', date_from: '',
date_to: '', date_to: '',
sub_search: '',
} }
}, },
@@ -93,7 +96,7 @@ const Filters = {
/** Reset all filters */ /** Reset all filters */
reset() { reset() {
this.state = { queue_id: '', state_id: '', priority_id: '', user_id: '', customer_user_id: '', date_from: '', date_to: '' }; this.state = { queue_id: '', state_id: '', priority_id: '', user_id: '', customer_user_id: '', date_from: '', date_to: '', sub_search: '' };
if (this.currentMode === 'my') { if (this.currentMode === 'my') {
const activeAgentId = localStorage.getItem('activeAgentId') || '1'; const activeAgentId = localStorage.getItem('activeAgentId') || '1';
this.state.user_id = activeAgentId; this.state.user_id = activeAgentId;
@@ -341,6 +344,13 @@ const Filters = {
<span class="filter-label">A Data/Ora</span> <span class="filter-label">A Data/Ora</span>
<input type="datetime-local" class="filter-select" data-filter="date_to" id="filter-date-to" value="${this.state.date_to || ''}" style="width: 170px; padding: 4px 8px; font-family: inherit; font-size: 0.85rem;" /> <input type="datetime-local" class="filter-select" data-filter="date_to" id="filter-date-to" value="${this.state.date_to || ''}" style="width: 170px; padding: 4px 8px; font-family: inherit; font-size: 0.85rem;" />
</div> </div>
<div class="filter-group">
<span class="filter-label">Cerca in Cache</span>
<div style="position:relative; display:inline-flex; align-items:center;">
<input type="text" class="filter-select form-input" data-filter="sub_search" id="filter-sub-search" value="${App.escapeHtml(this.state.sub_search || '')}" placeholder="Filtra testo (Invio)..." title="Digita e premi Invio per filtrare i risultati in cache" style="width: 140px; padding: 4px 22px 4px 8px; font-family: inherit; font-size: 0.85rem;" autocomplete="off" />
<button type="button" id="filter-sub-search-clear" style="position:absolute; right:6px; top:50%; transform:translateY(-50%); background:none; border:none; color:var(--text-muted); cursor:pointer; display:${this.state.sub_search ? 'flex' : 'none'}; align-items:center; justify-content:center; padding:0; font-size:12px; line-height:1;" title="Cancella testo in cache">✕</button>
</div>
</div>
<div class="filters-actions" style="display:flex; gap:6px; align-items:center;"> <div class="filters-actions" style="display:flex; gap:6px; align-items:center;">
<button class="btn btn-primary btn-xs" id="filter-refresh" title="Aggiorna lista ticket" style="display:inline-flex; align-items:center; gap:4px; height:26px; padding:0 10px; font-size:0.78rem;"> <button class="btn btn-primary btn-xs" id="filter-refresh" title="Aggiorna lista ticket" style="display:inline-flex; align-items:center; gap:4px; height:26px; padding:0 10px; font-size:0.78rem;">
🔄 Aggiorna 🔄 Aggiorna
@@ -363,6 +373,42 @@ const Filters = {
this.save(); this.save();
if (onFilterChange) onFilterChange(); if (onFilterChange) onFilterChange();
}); });
if (sel.tagName === 'INPUT' && sel.type === 'text') {
if (sel.dataset.filter === 'sub_search') {
const clearBtn = document.getElementById('filter-sub-search-clear');
sel.addEventListener('input', () => {
if (clearBtn) clearBtn.style.display = sel.value.trim() ? 'flex' : 'none';
});
sel.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.selectedPresetId = null;
this.state[e.target.dataset.filter] = e.target.value;
this.save();
if (onFilterChange) onFilterChange();
}
});
if (clearBtn) {
clearBtn.addEventListener('click', (e) => {
e.stopPropagation();
sel.value = '';
clearBtn.style.display = 'none';
this.selectedPresetId = null;
this.state.sub_search = '';
this.save();
if (onFilterChange) onFilterChange();
});
}
} else {
sel.addEventListener('input', (e) => {
this.selectedPresetId = null;
this.state[e.target.dataset.filter] = e.target.value;
this.save();
if (onFilterChange) onFilterChange();
});
}
}
}); });
// Helper to bind standard multiselect popover events // Helper to bind standard multiselect popover events
+42 -22
View File
@@ -24,36 +24,32 @@ const RicercaSpudorataView = {
Filters.load(); Filters.load();
const container = document.getElementById('view-container'); const container = document.getElementById('view-container');
container.innerHTML = ` if (!container) return;
<div class="loading-screen">
<div class="spinner"></div>
<p>Inizializzazione Ricerca Spudorata...</p>
</div>
`;
await App.ensureLookups(); const urlParams = new URLSearchParams((window.location.hash.split('?')[1]) || '');
const qParam = urlParams.get('q');
try { if (qParam && qParam !== this.searchTerm) {
const settings = await App.api('/api/dashboard/settings'); this.renderContainer(container);
if (settings && settings.tickets_per_page) { this.bindEvents();
this.perPage = settings.tickets_per_page; const input = document.getElementById('spudorata-search-input');
if (input) input.value = qParam;
this.executeSearch(qParam);
return;
} }
} catch (err) {
console.warn('Failed to load agent settings:', err); // Instant render from memory cache if already searched
if (this.hasSearched && this.cachedTickets) {
this.renderContainer(container);
this.bindEvents();
this.renderLocalResults();
return;
} }
this.renderContainer(container); this.renderContainer(container);
this.bindEvents(); this.bindEvents();
const urlParams = new URLSearchParams((window.location.hash.split('?')[1]) || ''); App.ensureLookups();
const qParam = urlParams.get('q');
if (qParam && !this.hasSearched) {
const input = document.getElementById('spudorata-search-input');
if (input) input.value = qParam;
this.executeSearch(qParam);
} else if (this.hasSearched && this.cachedTickets) {
this.renderLocalResults();
}
}, },
renderContainer(container) { renderContainer(container) {
@@ -324,6 +320,30 @@ const RicercaSpudorataView = {
let list = [...this.cachedTickets]; let list = [...this.cachedTickets];
const s = Filters.state; const s = Filters.state;
if (s.sub_search) {
const q = String(s.sub_search).toLowerCase().trim();
if (q) {
list = list.filter(t => {
const fields = [
t.tn,
t.title,
t.queue_name,
t.state_name,
t.priority_name,
t.owner_first,
t.owner_last,
`${t.owner_first || ''} ${t.owner_last || ''}`,
t.customer_first,
t.customer_last,
`${t.customer_first || ''} ${t.customer_last || ''}`,
t.customer_id,
t.customer_user_id
];
return fields.some(f => f && String(f).toLowerCase().includes(q));
});
}
}
if (s.state_id) { if (s.state_id) {
const selectedStates = String(s.state_id).split(',').filter(Boolean); const selectedStates = String(s.state_id).split(',').filter(Boolean);
list = list.filter(t => selectedStates.includes(String(t.ticket_state_id))); list = list.filter(t => selectedStates.includes(String(t.ticket_state_id)));
+190 -41
View File
@@ -9,18 +9,27 @@ const TicketListView = {
sortDir: 'DESC', sortDir: 'DESC',
selectedIds: new Set(), selectedIds: new Set(),
selectedOrder: [], selectedOrder: [],
lastQueryParams: null, rawTicketsByMode: {
cachedData: null, general: null,
my: null
},
lastSearchTermByMode: {
general: '',
my: ''
},
otrsBaseUrl: null,
needsRefresh: false, needsRefresh: false,
invalidateCache() { invalidateCache() {
this.lastQueryParams = null; this.rawTicketsByMode.general = null;
this.cachedData = null; this.rawTicketsByMode.my = null;
this.needsRefresh = true;
}, },
async render(force = false) { async render(force = false) {
const isMyTickets = window.location.hash.startsWith('#/tickets/my'); const isMyTickets = window.location.hash.startsWith('#/tickets/my');
Filters.currentMode = isMyTickets ? 'my' : 'general'; const newMode = isMyTickets ? 'my' : 'general';
Filters.currentMode = newMode;
Filters.load(); // Load state for current mode Filters.load(); // Load state for current mode
if (isMyTickets) { if (isMyTickets) {
@@ -29,40 +38,27 @@ const TicketListView = {
Filters.save(); Filters.save();
} }
const params = Filters.toQueryParams();
params.set('page', this.currentPage);
params.set('per_page', this.perPage);
params.set('sort_by', this.sortBy);
params.set('sort_dir', this.sortDir);
const searchInput = document.getElementById('global-search'); const searchInput = document.getElementById('global-search');
if (searchInput && searchInput.value.trim()) { const globalSearchVal = searchInput ? searchInput.value.trim() : '';
params.set('search', searchInput.value.trim());
}
const currentQueryString = `${Filters.currentMode}:${params.toString()}`;
const container = document.getElementById('view-container'); const container = document.getElementById('view-container');
if (!container) return;
// Return cached results instantly if query params haven't changed and no force refresh requested const cachedTickets = this.rawTicketsByMode[newMode];
if (!force && !this.needsRefresh && this.lastQueryParams === currentQueryString && this.cachedData) { const cachedSearchTerm = this.lastSearchTermByMode[newMode];
const currentHash = (window.location.hash || '#/dashboard').split('?')[0];
if (currentHash !== '#/tickets' && currentHash !== '#/tickets/my') {
return;
}
this.renderContent(container, this.cachedData);
this.bindEvents(this.cachedData);
return;
}
const shouldFetch = force ||
this.needsRefresh ||
!cachedTickets ||
cachedSearchTerm !== globalSearchVal;
if (shouldFetch) {
if (!container.querySelector('.ticket-table-wrapper') && !container.querySelector('.ticket-table')) { if (!container.querySelector('.ticket-table-wrapper') && !container.querySelector('.ticket-table')) {
container.innerHTML = '<div class="loading-screen"><div class="spinner"></div><p>Caricamento ticket...</p></div>'; container.innerHTML = '<div class="loading-screen"><div class="spinner"></div><p>Caricamento ticket...</p></div>';
} }
try { try {
// Fetch lookups for filter dropdowns
await App.ensureLookups(); await App.ensureLookups();
// Fetch agent settings for tickets_per_page
try { try {
const settings = await App.api('/api/dashboard/settings'); const settings = await App.api('/api/dashboard/settings');
if (settings && settings.tickets_per_page) { if (settings && settings.tickets_per_page) {
@@ -72,22 +68,27 @@ const TicketListView = {
console.warn('Failed to load agent settings:', err); console.warn('Failed to load agent settings:', err);
} }
const params = new URLSearchParams();
params.set('per_page', '2000');
if (isMyTickets) {
const activeAgentId = localStorage.getItem('activeAgentId') || '1';
params.set('user_id', activeAgentId);
}
if (globalSearchVal) {
params.set('search', globalSearchVal);
}
const data = await App.api(`/api/tickets?${params.toString()}`); const data = await App.api(`/api/tickets?${params.toString()}`);
// Route guard: if the user navigated away while the async search was in flight, do not overwrite the view
const currentHash = (window.location.hash || '#/dashboard').split('?')[0]; const currentHash = (window.location.hash || '#/dashboard').split('?')[0];
if (currentHash !== '#/tickets' && currentHash !== '#/tickets/my') { if (currentHash !== '#/tickets' && currentHash !== '#/tickets/my') {
return; return;
} }
// Save to cache this.rawTicketsByMode[newMode] = data.tickets || [];
this.lastQueryParams = currentQueryString; this.lastSearchTermByMode[newMode] = globalSearchVal;
this.cachedData = data; this.otrsBaseUrl = data.otrsBaseUrl || '';
this.needsRefresh = false; this.needsRefresh = false;
this.renderContent(container, data);
this.bindEvents(data);
} catch (err) { } catch (err) {
const currentHash = (window.location.hash || '#/dashboard').split('?')[0]; const currentHash = (window.location.hash || '#/dashboard').split('?')[0];
if (currentHash !== '#/tickets' && currentHash !== '#/tickets/my') { if (currentHash !== '#/tickets' && currentHash !== '#/tickets/my') {
@@ -101,7 +102,146 @@ const TicketListView = {
<div class="empty-state-sub">${App.escapeHtml(err.message)}</div> <div class="empty-state-sub">${App.escapeHtml(err.message)}</div>
</div> </div>
`; `;
return;
} }
}
this.renderLocalResults();
},
renderLocalResults() {
const container = document.getElementById('view-container');
if (!container) return;
const filtered = this.getFilteredTickets();
const total = filtered.length;
const totalPages = Math.ceil(total / this.perPage) || 1;
if (this.currentPage > totalPages) {
this.currentPage = 1;
}
const sorted = this.sortTickets(filtered);
const startIndex = (this.currentPage - 1) * this.perPage;
const paginatedTickets = sorted.slice(startIndex, startIndex + this.perPage);
const dataObj = {
tickets: paginatedTickets,
total: total,
page: this.currentPage,
per_page: this.perPage,
total_pages: totalPages,
otrsBaseUrl: this.otrsBaseUrl
};
this.renderContent(container, dataObj);
this.bindEvents(dataObj);
},
getFilteredTickets() {
const rawTickets = this.rawTicketsByMode[Filters.currentMode] || [];
if (!rawTickets || rawTickets.length === 0) return [];
let list = [...rawTickets];
const s = Filters.state;
// Sub-search text filter
if (s.sub_search) {
const q = String(s.sub_search).toLowerCase().trim();
if (q) {
list = list.filter(t => {
const fields = [
t.tn,
t.title,
t.queue_name,
t.state_name,
t.priority_name,
t.owner_first,
t.owner_last,
`${t.owner_first || ''} ${t.owner_last || ''}`,
t.customer_first,
t.customer_last,
`${t.customer_first || ''} ${t.customer_last || ''}`,
t.customer_id,
t.customer_user_id
];
return fields.some(f => f && String(f).toLowerCase().includes(q));
});
}
}
// State filter
if (s.state_id) {
const selectedStates = String(s.state_id).split(',').filter(Boolean);
list = list.filter(t => selectedStates.includes(String(t.ticket_state_id)));
}
// Queue filter
if (s.queue_id) {
const selectedQueues = String(s.queue_id).split(',').filter(Boolean);
list = list.filter(t => selectedQueues.includes(String(t.queue_id)));
}
// Priority filter
if (s.priority_id) {
const selectedPriorities = String(s.priority_id).split(',').filter(Boolean);
list = list.filter(t => selectedPriorities.includes(String(t.ticket_priority_id)));
}
// Owner filter (general mode)
if (s.user_id && Filters.currentMode !== 'my') {
const selectedUsers = String(s.user_id).split(',').filter(Boolean);
list = list.filter(t => selectedUsers.includes(String(t.user_id)));
}
// Customer user filter
if (s.customer_user_id) {
const selectedCustomers = String(s.customer_user_id).split(',').filter(Boolean).map(c => c.toLowerCase());
list = list.filter(t => t.customer_user_id && selectedCustomers.includes(String(t.customer_user_id).toLowerCase()));
}
// Date from
if (s.date_from) {
const fromDate = new Date(s.date_from);
list = list.filter(t => new Date(t.create_time) >= fromDate);
}
// Date to
if (s.date_to) {
const toDate = new Date(s.date_to + 'T23:59:59');
list = list.filter(t => new Date(t.create_time) <= toDate);
}
return list;
},
sortTickets(tickets) {
const list = [...tickets];
const key = this.sortBy;
const isAsc = this.sortDir === 'ASC';
list.sort((a, b) => {
let valA, valB;
if (key === 'queue') {
valA = a.queue_name || '';
valB = b.queue_name || '';
} else if (key === 'state') {
valA = a.state_name || '';
valB = b.state_name || '';
} else if (key === 'priority') {
valA = a.ticket_priority_id || 0;
valB = b.ticket_priority_id || 0;
} else {
valA = a[key] || '';
valB = b[key] || '';
}
if (typeof valA === 'string') {
return isAsc ? valA.localeCompare(valB, 'it', { sensitivity: 'base' }) : valB.localeCompare(valA, 'it', { sensitivity: 'base' });
}
return isAsc ? valA - valB : valB - valA;
});
return list;
}, },
renderContent(container, data) { renderContent(container, data) {
@@ -237,6 +377,7 @@ const TicketListView = {
<option value="50" ${per_page === 50 ? 'selected' : ''}>50</option> <option value="50" ${per_page === 50 ? 'selected' : ''}>50</option>
<option value="100" ${per_page === 100 ? 'selected' : ''}>100</option> <option value="100" ${per_page === 100 ? 'selected' : ''}>100</option>
<option value="200" ${per_page === 200 ? 'selected' : ''}>200</option> <option value="200" ${per_page === 200 ? 'selected' : ''}>200</option>
<option value="500" ${per_page === 500 ? 'selected' : ''}>500</option>
</select> </select>
</div> </div>
`; `;
@@ -286,9 +427,17 @@ const TicketListView = {
this.currentPage = 1; this.currentPage = 1;
this.selectedIds.clear(); this.selectedIds.clear();
this.selectedOrder = []; this.selectedOrder = [];
this.render(); this.renderLocalResults();
}); });
// Refresh button
const refreshFilterBtn = document.getElementById('filter-refresh');
if (refreshFilterBtn) {
refreshFilterBtn.addEventListener('click', () => {
this.render(true);
});
}
// Sort events // Sort events
document.querySelectorAll('.ticket-table th.sortable').forEach(th => { document.querySelectorAll('.ticket-table th.sortable').forEach(th => {
th.addEventListener('click', () => { th.addEventListener('click', () => {
@@ -300,7 +449,7 @@ const TicketListView = {
this.sortDir = 'DESC'; this.sortDir = 'DESC';
} }
this.currentPage = 1; this.currentPage = 1;
this.render(); this.renderLocalResults();
}); });
}); });
@@ -606,10 +755,10 @@ const TicketListView = {
// Pagination // Pagination
document.querySelectorAll('.pagination-btn[data-page]').forEach(btn => { document.querySelectorAll('.pagination-btn[data-page]').forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
this.currentPage = parseInt(btn.dataset.page); this.currentPage = parseInt(btn.dataset.page, 10);
this.selectedIds.clear(); this.selectedIds.clear();
this.selectedOrder = []; this.selectedOrder = [];
this.render(); this.renderLocalResults();
}); });
}); });
@@ -625,7 +774,7 @@ const TicketListView = {
Toast.success(`Righe per pagina aggiornate a ${newLimit}!`); Toast.success(`Righe per pagina aggiornate a ${newLimit}!`);
this.perPage = newLimit; this.perPage = newLimit;
this.currentPage = 1; this.currentPage = 1;
this.render(); this.renderLocalResults();
} catch (err) { } catch (err) {
Toast.error('Errore durante il salvataggio dell\'impostazione: ' + err.message); Toast.error('Errore durante il salvataggio dell\'impostazione: ' + err.message);
} }
+31
View File
@@ -236,6 +236,37 @@ router.get('/settings', (req, res) => {
} }
}); });
// POST /api/dashboard/settings — Save settings for the agent
router.post('/settings', (req, res) => {
const activeAgentId = parseInt(req.headers['x-agent-id'], 10) || 1;
const { preview_limit, tickets_per_page } = req.body || {};
try {
const existing = db.prepare("SELECT agent_id, preview_limit, tickets_per_page FROM agent_settings WHERE agent_id = ?").get(activeAgentId);
const newPreviewLimit = preview_limit !== undefined ? parseInt(preview_limit, 10) : (existing ? existing.preview_limit : 10);
const newTicketsPerPage = tickets_per_page !== undefined ? parseInt(tickets_per_page, 10) : (existing ? existing.tickets_per_page : 50);
if (existing) {
db.prepare(`
UPDATE agent_settings
SET preview_limit = ?, tickets_per_page = ?
WHERE agent_id = ?
`).run(newPreviewLimit, newTicketsPerPage, activeAgentId);
} else {
db.prepare(`
INSERT INTO agent_settings (agent_id, preview_limit, tickets_per_page)
VALUES (?, ?, ?)
`).run(activeAgentId, newPreviewLimit, newTicketsPerPage);
}
res.json({ success: true, preview_limit: newPreviewLimit, tickets_per_page: newTicketsPerPage });
} catch (err) {
console.error('Error saving agent settings:', err);
res.status(500).json({ error: err.message });
}
});
// GET /api/dashboard/chart-lines // GET /api/dashboard/chart-lines
router.get('/chart-lines', (req, res) => { router.get('/chart-lines', (req, res) => {
try { try {
+4 -3
View File
@@ -103,7 +103,7 @@ router.get('/', async (req, res) => {
try { try {
const { const {
queue_id, state_id, priority_id, user_id, type_id, customer_user_id, queue_id, state_id, priority_id, user_id, type_id, customer_user_id,
search, sort_by = 'create_time', sort_dir = 'DESC', search, sub_search, sort_by = 'create_time', sort_dir = 'DESC',
page = 1, per_page = 50, page = 1, per_page = 50,
date_from, date_to date_from, date_to
} = req.query; } = req.query;
@@ -209,7 +209,8 @@ router.get('/', async (req, res) => {
conditions.push(`t.create_time <= $${paramIdx++}`); conditions.push(`t.create_time <= $${paramIdx++}`);
params.push(new Date(date_to)); params.push(new Date(date_to));
} }
if (search) { const searchTerm = search || sub_search;
if (searchTerm) {
conditions.push(`( conditions.push(`(
t.title ILIKE $${paramIdx} OR t.title ILIKE $${paramIdx} OR
t.tn ILIKE $${paramIdx} OR t.tn ILIKE $${paramIdx} OR
@@ -222,7 +223,7 @@ router.get('/', async (req, res) => {
) )
) )
)`); )`);
params.push(`%${search}%`); params.push(`%${searchTerm}%`);
paramIdx++; paramIdx++;
} }