mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
item handling
This commit is contained in:
@ -15,6 +15,7 @@ export default defineNuxtConfig({
|
|||||||
'@nuxt/icon',
|
'@nuxt/icon',
|
||||||
'nuxt-snackbar',
|
'nuxt-snackbar',
|
||||||
'@nuxt/eslint',
|
'@nuxt/eslint',
|
||||||
|
'@nuxt/image',
|
||||||
],
|
],
|
||||||
|
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
"@libsql/client": "^0.15.8",
|
"@libsql/client": "^0.15.8",
|
||||||
"@nuxt/eslint": "1.4.1",
|
"@nuxt/eslint": "1.4.1",
|
||||||
"@nuxt/icon": "^1.13.0",
|
"@nuxt/icon": "^1.13.0",
|
||||||
|
"@nuxt/image": "1.10.0",
|
||||||
"@nuxtjs/i18n": "^9.5.5",
|
"@nuxtjs/i18n": "^9.5.5",
|
||||||
"@pinia/nuxt": "^0.11.0",
|
"@pinia/nuxt": "^0.11.0",
|
||||||
"@tailwindcss/vite": "^4.1.8",
|
"@tailwindcss/vite": "^4.1.8",
|
||||||
|
|||||||
390
pnpm-lock.yaml
generated
390
pnpm-lock.yaml
generated
@ -17,6 +17,9 @@ importers:
|
|||||||
'@nuxt/icon':
|
'@nuxt/icon':
|
||||||
specifier: ^1.13.0
|
specifier: ^1.13.0
|
||||||
version: 1.13.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.40.0)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
|
version: 1.13.0(magicast@0.3.5)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.40.0)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
|
||||||
|
'@nuxt/image':
|
||||||
|
specifier: 1.10.0
|
||||||
|
version: 1.10.0(db0@0.3.2(@libsql/client@0.15.8)(drizzle-orm@0.43.1(@libsql/client@0.15.8)))(ioredis@5.6.1)(magicast@0.3.5)
|
||||||
'@nuxtjs/i18n':
|
'@nuxtjs/i18n':
|
||||||
specifier: ^9.5.5
|
specifier: ^9.5.5
|
||||||
version: 9.5.5(@vue/compiler-dom@3.5.16)(eslint@9.28.0(jiti@2.4.2))(magicast@0.3.5)(rollup@4.41.1)(vue@3.5.16(typescript@5.8.3))
|
version: 9.5.5(@vue/compiler-dom@3.5.16)(eslint@9.28.0(jiti@2.4.2))(magicast@0.3.5)(rollup@4.41.1)(vue@3.5.16(typescript@5.8.3))
|
||||||
@ -785,6 +788,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==}
|
resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@fastify/accept-negotiator@1.1.0':
|
||||||
|
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
'@fastify/busboy@3.1.1':
|
'@fastify/busboy@3.1.1':
|
||||||
resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==}
|
resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==}
|
||||||
|
|
||||||
@ -1140,6 +1147,10 @@ packages:
|
|||||||
'@nuxt/icon@1.13.0':
|
'@nuxt/icon@1.13.0':
|
||||||
resolution: {integrity: sha512-Sft1DZj/U5Up60DMnhp+hRDNDXRkMhwHocxgDkDkAPBxqR8PRyvzxcMIy3rjGMu0s+fB6ZdLs6vtaWzjWuerQQ==}
|
resolution: {integrity: sha512-Sft1DZj/U5Up60DMnhp+hRDNDXRkMhwHocxgDkDkAPBxqR8PRyvzxcMIy3rjGMu0s+fB6ZdLs6vtaWzjWuerQQ==}
|
||||||
|
|
||||||
|
'@nuxt/image@1.10.0':
|
||||||
|
resolution: {integrity: sha512-/B58GeEmme7bkmQUrXzEw8P9sJb9BkMaYZqLDtq8ZdDLEddE3P4nVya8RQPB+p4b7EdqWajpPqdy1A2ZPLev/A==}
|
||||||
|
engines: {node: '>=18.20.6'}
|
||||||
|
|
||||||
'@nuxt/kit@3.17.4':
|
'@nuxt/kit@3.17.4':
|
||||||
resolution: {integrity: sha512-l+hY8sy2XFfg3PigZj+PTu6+KIJzmbACTRimn1ew/gtCz+F38f6KTF4sMRTN5CUxiB8TRENgEonASmkAWfpO9Q==}
|
resolution: {integrity: sha512-l+hY8sy2XFfg3PigZj+PTu6+KIJzmbACTRimn1ew/gtCz+F38f6KTF4sMRTN5CUxiB8TRENgEonASmkAWfpO9Q==}
|
||||||
engines: {node: '>=18.12.0'}
|
engines: {node: '>=18.12.0'}
|
||||||
@ -2292,6 +2303,33 @@ packages:
|
|||||||
bare-events@2.5.4:
|
bare-events@2.5.4:
|
||||||
resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==}
|
resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==}
|
||||||
|
|
||||||
|
bare-fs@4.1.5:
|
||||||
|
resolution: {integrity: sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==}
|
||||||
|
engines: {bare: '>=1.16.0'}
|
||||||
|
peerDependencies:
|
||||||
|
bare-buffer: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bare-buffer:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
bare-os@3.6.1:
|
||||||
|
resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==}
|
||||||
|
engines: {bare: '>=1.14.0'}
|
||||||
|
|
||||||
|
bare-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
|
||||||
|
|
||||||
|
bare-stream@2.6.5:
|
||||||
|
resolution: {integrity: sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==}
|
||||||
|
peerDependencies:
|
||||||
|
bare-buffer: '*'
|
||||||
|
bare-events: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bare-buffer:
|
||||||
|
optional: true
|
||||||
|
bare-events:
|
||||||
|
optional: true
|
||||||
|
|
||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
@ -2301,6 +2339,9 @@ packages:
|
|||||||
birpc@2.3.0:
|
birpc@2.3.0:
|
||||||
resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==}
|
resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||||
|
|
||||||
boolbase@1.0.0:
|
boolbase@1.0.0:
|
||||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||||
|
|
||||||
@ -2329,6 +2370,9 @@ packages:
|
|||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||||
|
|
||||||
@ -2391,6 +2435,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
|
|
||||||
|
chownr@1.1.4:
|
||||||
|
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||||
|
|
||||||
chownr@3.0.0:
|
chownr@3.0.0:
|
||||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -2437,6 +2484,10 @@ packages:
|
|||||||
color@3.2.1:
|
color@3.2.1:
|
||||||
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
|
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
|
||||||
|
|
||||||
|
color@4.2.3:
|
||||||
|
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||||
|
engines: {node: '>=12.5.0'}
|
||||||
|
|
||||||
colord@2.9.3:
|
colord@2.9.3:
|
||||||
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
|
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
|
||||||
|
|
||||||
@ -2565,6 +2616,9 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
cssfilter@0.0.10:
|
||||||
|
resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==}
|
||||||
|
|
||||||
cssnano-preset-default@7.0.7:
|
cssnano-preset-default@7.0.7:
|
||||||
resolution: {integrity: sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==}
|
resolution: {integrity: sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==}
|
||||||
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0}
|
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0}
|
||||||
@ -2640,10 +2694,18 @@ packages:
|
|||||||
decache@4.6.2:
|
decache@4.6.2:
|
||||||
resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==}
|
resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==}
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
dedupe@4.0.3:
|
dedupe@4.0.3:
|
||||||
resolution: {integrity: sha512-WMo1AIq5MSstJ5DhVVccdue22MEpaxzf0tdznjXgWrfei9F3aNYP7xhbKgi1U8JlvTU4RyhUSLI63jt7PUipBQ==}
|
resolution: {integrity: sha512-WMo1AIq5MSstJ5DhVVccdue22MEpaxzf0tdznjXgWrfei9F3aNYP7xhbKgi1U8JlvTU4RyhUSLI63jt7PUipBQ==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
|
||||||
|
deep-extend@0.6.0:
|
||||||
|
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||||
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
@ -3114,6 +3176,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||||
engines: {node: '>=16.17'}
|
engines: {node: '>=16.17'}
|
||||||
|
|
||||||
|
expand-template@2.0.3:
|
||||||
|
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
exsolve@1.0.5:
|
exsolve@1.0.5:
|
||||||
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
||||||
|
|
||||||
@ -3220,6 +3286,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
fs-constants@1.0.0:
|
||||||
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
|
|
||||||
fs.realpath@1.0.0:
|
fs.realpath@1.0.0:
|
||||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
|
|
||||||
@ -3279,6 +3348,9 @@ packages:
|
|||||||
git-url-parse@16.1.0:
|
git-url-parse@16.1.0:
|
||||||
resolution: {integrity: sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==}
|
resolution: {integrity: sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==}
|
||||||
|
|
||||||
|
github-from-package@0.0.0:
|
||||||
|
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@ -3424,6 +3496,9 @@ packages:
|
|||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
|
ini@1.3.8:
|
||||||
|
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||||
|
|
||||||
ini@4.1.1:
|
ini@4.1.1:
|
||||||
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
|
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
@ -3432,6 +3507,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==}
|
resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==}
|
||||||
engines: {node: '>=12.22.0'}
|
engines: {node: '>=12.22.0'}
|
||||||
|
|
||||||
|
ipx@2.1.0:
|
||||||
|
resolution: {integrity: sha512-AVnPGXJ8L41vjd11Z4akIF2yd14636Klxul3tBySxHA6PKfCOQPxBDkCFK5zcWh0z/keR6toh1eg8qzdBVUgdA==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
iron-webcrypto@1.2.1:
|
iron-webcrypto@1.2.1:
|
||||||
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
|
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
|
||||||
|
|
||||||
@ -3840,6 +3919,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
mimic-response@3.1.0:
|
||||||
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
min-indent@1.0.1:
|
min-indent@1.0.1:
|
||||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -3873,6 +3956,9 @@ packages:
|
|||||||
mitt@3.0.1:
|
mitt@3.0.1:
|
||||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3:
|
||||||
|
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||||
|
|
||||||
mkdirp@3.0.1:
|
mkdirp@3.0.1:
|
||||||
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -3912,6 +3998,9 @@ packages:
|
|||||||
nanotar@0.2.0:
|
nanotar@0.2.0:
|
||||||
resolution: {integrity: sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==}
|
resolution: {integrity: sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==}
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0:
|
||||||
|
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||||
|
|
||||||
napi-postinstall@0.2.4:
|
napi-postinstall@0.2.4:
|
||||||
resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==}
|
resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==}
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||||
@ -3934,6 +4023,13 @@ packages:
|
|||||||
xml2js:
|
xml2js:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
node-abi@3.75.0:
|
||||||
|
resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
node-addon-api@6.1.0:
|
||||||
|
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
|
||||||
|
|
||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
@ -4402,6 +4498,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==}
|
resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
precinct@12.2.0:
|
precinct@12.2.0:
|
||||||
resolution: {integrity: sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==}
|
resolution: {integrity: sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -4465,6 +4566,10 @@ packages:
|
|||||||
rc9@2.1.2:
|
rc9@2.1.2:
|
||||||
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
read-package-up@11.0.0:
|
read-package-up@11.0.0:
|
||||||
resolution: {integrity: sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==}
|
resolution: {integrity: sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@ -4620,6 +4725,10 @@ packages:
|
|||||||
setprototypeof@1.2.0:
|
setprototypeof@1.2.0:
|
||||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
|
|
||||||
|
sharp@0.32.6:
|
||||||
|
resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
|
||||||
|
engines: {node: '>=14.15.0'}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -4652,6 +4761,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
simple-concat@1.0.1:
|
||||||
|
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||||
|
|
||||||
simple-git@3.27.0:
|
simple-git@3.27.0:
|
||||||
resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==}
|
resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==}
|
||||||
|
|
||||||
@ -4755,6 +4870,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
|
resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1:
|
||||||
|
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
strip-json-comments@3.1.1:
|
strip-json-comments@3.1.1:
|
||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -4813,6 +4932,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
|
resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
tar-fs@2.1.3:
|
||||||
|
resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==}
|
||||||
|
|
||||||
|
tar-fs@3.0.9:
|
||||||
|
resolution: {integrity: sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==}
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tar-stream@3.1.7:
|
tar-stream@3.1.7:
|
||||||
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
|
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
|
||||||
|
|
||||||
@ -4893,6 +5022,9 @@ packages:
|
|||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@ -5285,6 +5417,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
xss@1.0.15:
|
||||||
|
resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==}
|
||||||
|
engines: {node: '>= 0.10.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
y18n@5.0.8:
|
y18n@5.0.8:
|
||||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -5884,6 +6021,9 @@ snapshots:
|
|||||||
'@eslint/core': 0.14.0
|
'@eslint/core': 0.14.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
|
'@fastify/accept-negotiator@1.1.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@fastify/busboy@3.1.1': {}
|
'@fastify/busboy@3.1.1': {}
|
||||||
|
|
||||||
'@floating-ui/core@1.7.0':
|
'@floating-ui/core@1.7.0':
|
||||||
@ -6457,6 +6597,42 @@ snapshots:
|
|||||||
- vite
|
- vite
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@nuxt/image@1.10.0(db0@0.3.2(@libsql/client@0.15.8)(drizzle-orm@0.43.1(@libsql/client@0.15.8)))(ioredis@5.6.1)(magicast@0.3.5)':
|
||||||
|
dependencies:
|
||||||
|
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||||
|
consola: 3.4.2
|
||||||
|
defu: 6.1.4
|
||||||
|
h3: 1.15.3
|
||||||
|
image-meta: 0.2.1
|
||||||
|
knitwork: 1.2.0
|
||||||
|
ohash: 2.0.11
|
||||||
|
pathe: 2.0.3
|
||||||
|
std-env: 3.9.0
|
||||||
|
ufo: 1.6.1
|
||||||
|
optionalDependencies:
|
||||||
|
ipx: 2.1.0(db0@0.3.2(@libsql/client@0.15.8)(drizzle-orm@0.43.1(@libsql/client@0.15.8)))(ioredis@5.6.1)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@azure/app-configuration'
|
||||||
|
- '@azure/cosmos'
|
||||||
|
- '@azure/data-tables'
|
||||||
|
- '@azure/identity'
|
||||||
|
- '@azure/keyvault-secrets'
|
||||||
|
- '@azure/storage-blob'
|
||||||
|
- '@capacitor/preferences'
|
||||||
|
- '@deno/kv'
|
||||||
|
- '@netlify/blobs'
|
||||||
|
- '@planetscale/database'
|
||||||
|
- '@upstash/redis'
|
||||||
|
- '@vercel/blob'
|
||||||
|
- '@vercel/kv'
|
||||||
|
- aws4fetch
|
||||||
|
- bare-buffer
|
||||||
|
- db0
|
||||||
|
- idb-keyval
|
||||||
|
- ioredis
|
||||||
|
- magicast
|
||||||
|
- uploadthing
|
||||||
|
|
||||||
'@nuxt/kit@3.17.4(magicast@0.3.5)':
|
'@nuxt/kit@3.17.4(magicast@0.3.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
c12: 3.0.4(magicast@0.3.5)
|
c12: 3.0.4(magicast@0.3.5)
|
||||||
@ -7652,6 +7828,28 @@ snapshots:
|
|||||||
bare-events@2.5.4:
|
bare-events@2.5.4:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
bare-fs@4.1.5:
|
||||||
|
dependencies:
|
||||||
|
bare-events: 2.5.4
|
||||||
|
bare-path: 3.0.0
|
||||||
|
bare-stream: 2.6.5(bare-events@2.5.4)
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
bare-os@3.6.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
bare-path@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
bare-os: 3.6.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
bare-stream@2.6.5(bare-events@2.5.4):
|
||||||
|
dependencies:
|
||||||
|
streamx: 2.22.0
|
||||||
|
optionalDependencies:
|
||||||
|
bare-events: 2.5.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
@ -7660,6 +7858,13 @@ snapshots:
|
|||||||
|
|
||||||
birpc@2.3.0: {}
|
birpc@2.3.0: {}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
buffer: 5.7.1
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
boolbase@1.0.0: {}
|
boolbase@1.0.0: {}
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
@ -7688,6 +7893,12 @@ snapshots:
|
|||||||
|
|
||||||
buffer-from@1.1.2: {}
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
ieee754: 1.2.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
base64-js: 1.5.1
|
||||||
@ -7757,6 +7968,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
|
|
||||||
|
chownr@1.1.4:
|
||||||
|
optional: true
|
||||||
|
|
||||||
chownr@3.0.0: {}
|
chownr@3.0.0: {}
|
||||||
|
|
||||||
ci-info@4.2.0: {}
|
ci-info@4.2.0: {}
|
||||||
@ -7805,6 +8019,12 @@ snapshots:
|
|||||||
color-convert: 1.9.3
|
color-convert: 1.9.3
|
||||||
color-string: 1.9.1
|
color-string: 1.9.1
|
||||||
|
|
||||||
|
color@4.2.3:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
color-string: 1.9.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
colord@2.9.3: {}
|
colord@2.9.3: {}
|
||||||
|
|
||||||
colorspace@1.1.4:
|
colorspace@1.1.4:
|
||||||
@ -7916,6 +8136,9 @@ snapshots:
|
|||||||
|
|
||||||
cssesc@3.0.0: {}
|
cssesc@3.0.0: {}
|
||||||
|
|
||||||
|
cssfilter@0.0.10:
|
||||||
|
optional: true
|
||||||
|
|
||||||
cssnano-preset-default@7.0.7(postcss@8.5.4):
|
cssnano-preset-default@7.0.7(postcss@8.5.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.25.0
|
browserslist: 4.25.0
|
||||||
@ -7988,8 +8211,16 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
callsite: 1.0.0
|
callsite: 1.0.0
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
dedupe@4.0.3: {}
|
dedupe@4.0.3: {}
|
||||||
|
|
||||||
|
deep-extend@0.6.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
deepmerge@4.3.1: {}
|
deepmerge@4.3.1: {}
|
||||||
@ -8489,6 +8720,9 @@ snapshots:
|
|||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
strip-final-newline: 3.0.0
|
strip-final-newline: 3.0.0
|
||||||
|
|
||||||
|
expand-template@2.0.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
exsolve@1.0.5: {}
|
exsolve@1.0.5: {}
|
||||||
|
|
||||||
externality@1.0.2:
|
externality@1.0.2:
|
||||||
@ -8596,6 +8830,9 @@ snapshots:
|
|||||||
|
|
||||||
fresh@2.0.0: {}
|
fresh@2.0.0: {}
|
||||||
|
|
||||||
|
fs-constants@1.0.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fs.realpath@1.0.0: {}
|
fs.realpath@1.0.0: {}
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
@ -8662,6 +8899,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
git-up: 8.1.1
|
git-up: 8.1.1
|
||||||
|
|
||||||
|
github-from-package@0.0.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -8805,6 +9045,9 @@ snapshots:
|
|||||||
|
|
||||||
inherits@2.0.4: {}
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
|
ini@1.3.8:
|
||||||
|
optional: true
|
||||||
|
|
||||||
ini@4.1.1: {}
|
ini@4.1.1: {}
|
||||||
|
|
||||||
ioredis@5.6.1:
|
ioredis@5.6.1:
|
||||||
@ -8821,6 +9064,46 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
ipx@2.1.0(db0@0.3.2(@libsql/client@0.15.8)(drizzle-orm@0.43.1(@libsql/client@0.15.8)))(ioredis@5.6.1):
|
||||||
|
dependencies:
|
||||||
|
'@fastify/accept-negotiator': 1.1.0
|
||||||
|
citty: 0.1.6
|
||||||
|
consola: 3.4.2
|
||||||
|
defu: 6.1.4
|
||||||
|
destr: 2.0.5
|
||||||
|
etag: 1.8.1
|
||||||
|
h3: 1.15.3
|
||||||
|
image-meta: 0.2.1
|
||||||
|
listhen: 1.9.0
|
||||||
|
ofetch: 1.4.1
|
||||||
|
pathe: 1.1.2
|
||||||
|
sharp: 0.32.6
|
||||||
|
svgo: 3.3.2
|
||||||
|
ufo: 1.6.1
|
||||||
|
unstorage: 1.16.0(db0@0.3.2(@libsql/client@0.15.8)(drizzle-orm@0.43.1(@libsql/client@0.15.8)))(ioredis@5.6.1)
|
||||||
|
xss: 1.0.15
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@azure/app-configuration'
|
||||||
|
- '@azure/cosmos'
|
||||||
|
- '@azure/data-tables'
|
||||||
|
- '@azure/identity'
|
||||||
|
- '@azure/keyvault-secrets'
|
||||||
|
- '@azure/storage-blob'
|
||||||
|
- '@capacitor/preferences'
|
||||||
|
- '@deno/kv'
|
||||||
|
- '@netlify/blobs'
|
||||||
|
- '@planetscale/database'
|
||||||
|
- '@upstash/redis'
|
||||||
|
- '@vercel/blob'
|
||||||
|
- '@vercel/kv'
|
||||||
|
- aws4fetch
|
||||||
|
- bare-buffer
|
||||||
|
- db0
|
||||||
|
- idb-keyval
|
||||||
|
- ioredis
|
||||||
|
- uploadthing
|
||||||
|
optional: true
|
||||||
|
|
||||||
iron-webcrypto@1.2.1: {}
|
iron-webcrypto@1.2.1: {}
|
||||||
|
|
||||||
is-arrayish@0.3.2: {}
|
is-arrayish@0.3.2: {}
|
||||||
@ -9167,6 +9450,9 @@ snapshots:
|
|||||||
|
|
||||||
mimic-fn@4.0.0: {}
|
mimic-fn@4.0.0: {}
|
||||||
|
|
||||||
|
mimic-response@3.1.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
min-indent@1.0.1: {}
|
min-indent@1.0.1: {}
|
||||||
|
|
||||||
minimatch@10.0.1:
|
minimatch@10.0.1:
|
||||||
@ -9195,6 +9481,9 @@ snapshots:
|
|||||||
|
|
||||||
mitt@3.0.1: {}
|
mitt@3.0.1: {}
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
mkdirp@3.0.1: {}
|
mkdirp@3.0.1: {}
|
||||||
|
|
||||||
mlly@1.7.4:
|
mlly@1.7.4:
|
||||||
@ -9223,6 +9512,9 @@ snapshots:
|
|||||||
|
|
||||||
nanotar@0.2.0: {}
|
nanotar@0.2.0: {}
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
napi-postinstall@0.2.4: {}
|
napi-postinstall@0.2.4: {}
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
@ -9336,6 +9628,14 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- uploadthing
|
- uploadthing
|
||||||
|
|
||||||
|
node-abi@3.75.0:
|
||||||
|
dependencies:
|
||||||
|
semver: 7.7.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
node-addon-api@6.1.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
node-addon-api@7.1.1: {}
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
node-domexception@1.0.0: {}
|
||||||
@ -9921,6 +10221,22 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.0.4
|
||||||
|
expand-template: 2.0.3
|
||||||
|
github-from-package: 0.0.0
|
||||||
|
minimist: 1.2.8
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
napi-build-utils: 2.0.0
|
||||||
|
node-abi: 3.75.0
|
||||||
|
pump: 3.0.2
|
||||||
|
rc: 1.2.8
|
||||||
|
simple-get: 4.0.1
|
||||||
|
tar-fs: 2.1.3
|
||||||
|
tunnel-agent: 0.6.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
precinct@12.2.0:
|
precinct@12.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dependents/detective-less': 5.0.1
|
'@dependents/detective-less': 5.0.1
|
||||||
@ -9988,6 +10304,14 @@ snapshots:
|
|||||||
defu: 6.1.4
|
defu: 6.1.4
|
||||||
destr: 2.0.5
|
destr: 2.0.5
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
dependencies:
|
||||||
|
deep-extend: 0.6.0
|
||||||
|
ini: 1.3.8
|
||||||
|
minimist: 1.2.8
|
||||||
|
strip-json-comments: 2.0.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
read-package-up@11.0.0:
|
read-package-up@11.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up-simple: 1.0.1
|
find-up-simple: 1.0.1
|
||||||
@ -10175,6 +10499,20 @@ snapshots:
|
|||||||
|
|
||||||
setprototypeof@1.2.0: {}
|
setprototypeof@1.2.0: {}
|
||||||
|
|
||||||
|
sharp@0.32.6:
|
||||||
|
dependencies:
|
||||||
|
color: 4.2.3
|
||||||
|
detect-libc: 2.0.4
|
||||||
|
node-addon-api: 6.1.0
|
||||||
|
prebuild-install: 7.1.3
|
||||||
|
semver: 7.7.2
|
||||||
|
simple-get: 4.0.1
|
||||||
|
tar-fs: 3.0.9
|
||||||
|
tunnel-agent: 0.6.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bare-buffer
|
||||||
|
optional: true
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
@ -10213,6 +10551,16 @@ snapshots:
|
|||||||
|
|
||||||
signal-exit@4.1.0: {}
|
signal-exit@4.1.0: {}
|
||||||
|
|
||||||
|
simple-concat@1.0.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
decompress-response: 6.0.0
|
||||||
|
once: 1.4.0
|
||||||
|
simple-concat: 1.0.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
simple-git@3.27.0:
|
simple-git@3.27.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@kwsites/file-exists': 1.1.1
|
'@kwsites/file-exists': 1.1.1
|
||||||
@ -10320,6 +10668,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
min-indent: 1.0.1
|
min-indent: 1.0.1
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
strip-literal@3.0.0:
|
strip-literal@3.0.0:
|
||||||
@ -10370,6 +10721,34 @@ snapshots:
|
|||||||
|
|
||||||
tapable@2.2.2: {}
|
tapable@2.2.2: {}
|
||||||
|
|
||||||
|
tar-fs@2.1.3:
|
||||||
|
dependencies:
|
||||||
|
chownr: 1.1.4
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
pump: 3.0.2
|
||||||
|
tar-stream: 2.2.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
tar-fs@3.0.9:
|
||||||
|
dependencies:
|
||||||
|
pump: 3.0.2
|
||||||
|
tar-stream: 3.1.7
|
||||||
|
optionalDependencies:
|
||||||
|
bare-fs: 4.1.5
|
||||||
|
bare-path: 3.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bare-buffer
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
bl: 4.1.0
|
||||||
|
end-of-stream: 1.4.4
|
||||||
|
fs-constants: 1.0.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
tar-stream@3.1.7:
|
tar-stream@3.1.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
b4a: 1.6.7
|
b4a: 1.6.7
|
||||||
@ -10444,6 +10823,11 @@ snapshots:
|
|||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
@ -10841,6 +11225,12 @@ snapshots:
|
|||||||
|
|
||||||
xml-name-validator@4.0.0: {}
|
xml-name-validator@4.0.0: {}
|
||||||
|
|
||||||
|
xss@1.0.15:
|
||||||
|
dependencies:
|
||||||
|
commander: 2.20.3
|
||||||
|
cssfilter: 0.0.10
|
||||||
|
optional: true
|
||||||
|
|
||||||
y18n@5.0.8: {}
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|||||||
BIN
public/horn-fragend.png
Normal file
BIN
public/horn-fragend.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@ -14,24 +14,24 @@
|
|||||||
"core:window:allow-show",
|
"core:window:allow-show",
|
||||||
"core:window:default",
|
"core:window:default",
|
||||||
"dialog:default",
|
"dialog:default",
|
||||||
"fs:allow-appdata-read-recursive",
|
|
||||||
"fs:allow-appdata-write-recursive",
|
|
||||||
"fs:allow-appconfig-read-recursive",
|
"fs:allow-appconfig-read-recursive",
|
||||||
"fs:allow-appconfig-write-recursive",
|
"fs:allow-appconfig-write-recursive",
|
||||||
|
"fs:allow-appdata-read-recursive",
|
||||||
|
"fs:allow-appdata-write-recursive",
|
||||||
"fs:allow-read-file",
|
"fs:allow-read-file",
|
||||||
"fs:allow-resource-read-recursive",
|
"fs:allow-resource-read-recursive",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
"http:allow-fetch-send",
|
"http:allow-fetch-send",
|
||||||
"http:allow-fetch",
|
"http:allow-fetch",
|
||||||
"http:default",
|
"http:default",
|
||||||
|
"notification:allow-create-channel",
|
||||||
|
"notification:allow-list-channels",
|
||||||
|
"notification:allow-notify",
|
||||||
|
"notification:default",
|
||||||
"opener:allow-open-url",
|
"opener:allow-open-url",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
"os:default",
|
|
||||||
"os:allow-hostname",
|
"os:allow-hostname",
|
||||||
"store:default",
|
"os:default",
|
||||||
"notification:default",
|
"store:default"
|
||||||
"notification:allow-notify",
|
|
||||||
"notification:allow-create-channel",
|
|
||||||
"notification:allow-list-channels"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
40
src-tauri/database/migrations/0008_faulty_mercury.sql
Normal file
40
src-tauri/database/migrations/0008_faulty_mercury.sql
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
ALTER TABLE `haex_passwords_items` RENAME TO `haex_passwords_item_details`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `haex_passwords_items_key_values` RENAME TO `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_haex_passwords_item_key_values` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`item_id` text,
|
||||||
|
`key` text,
|
||||||
|
`value` text,
|
||||||
|
`updated_at` integer,
|
||||||
|
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_haex_passwords_item_key_values`("id", "item_id", "key", "value", "updated_at") SELECT "id", "item_id", "key", "value", "updated_at" FROM `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||||
|
DROP TABLE `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_haex_passwords_item_key_values` RENAME TO `haex_passwords_item_key_values`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_haex_passwords_group_items` (
|
||||||
|
`group_id` text,
|
||||||
|
`item_id` text,
|
||||||
|
PRIMARY KEY(`item_id`, `group_id`),
|
||||||
|
FOREIGN KEY (`group_id`) REFERENCES `haex_passwords_groups`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_haex_passwords_group_items`("group_id", "item_id") SELECT "group_id", "item_id" FROM `haex_passwords_group_items`;--> statement-breakpoint
|
||||||
|
DROP TABLE `haex_passwords_group_items`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_haex_passwords_group_items` RENAME TO `haex_passwords_group_items`;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_haex_passwords_item_history` (
|
||||||
|
`id` text PRIMARY KEY NOT NULL,
|
||||||
|
`item_id` text,
|
||||||
|
`changed_property` text,
|
||||||
|
`old_value` text,
|
||||||
|
`new_value` text,
|
||||||
|
`created_at` text DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
FOREIGN KEY (`item_id`) REFERENCES `haex_passwords_item_details`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_haex_passwords_item_history`("id", "item_id", "changed_property", "old_value", "new_value", "created_at") SELECT "id", "item_id", "changed_property", "old_value", "new_value", "created_at" FROM `haex_passwords_item_history`;--> statement-breakpoint
|
||||||
|
DROP TABLE `haex_passwords_item_history`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_haex_passwords_item_history` RENAME TO `haex_passwords_item_history`;
|
||||||
630
src-tauri/database/migrations/meta/0008_snapshot.json
Normal file
630
src-tauri/database/migrations/meta/0008_snapshot.json
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "c4edecb8-6aef-49e2-8498-0c4b74653c75",
|
||||||
|
"prevId": "47f309cf-dabd-4f19-b87a-ed73d0e97781",
|
||||||
|
"tables": {
|
||||||
|
"haex_extensions": {
|
||||||
|
"name": "haex_extensions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "author",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"name": "url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"name": "version",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_extensions_permissions": {
|
||||||
|
"name": "haex_extensions_permissions",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"extension_id": {
|
||||||
|
"name": "extension_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"name": "resource",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"operation": {
|
||||||
|
"name": "operation",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"name": "path",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(CURRENT_TIMESTAMP)"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"haex_extensions_permissions_extension_id_resource_operation_path_unique": {
|
||||||
|
"name": "haex_extensions_permissions_extension_id_resource_operation_path_unique",
|
||||||
|
"columns": [
|
||||||
|
"extension_id",
|
||||||
|
"resource",
|
||||||
|
"operation",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"isUnique": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {
|
||||||
|
"haex_extensions_permissions_extension_id_haex_extensions_id_fk": {
|
||||||
|
"name": "haex_extensions_permissions_extension_id_haex_extensions_id_fk",
|
||||||
|
"tableFrom": "haex_extensions_permissions",
|
||||||
|
"tableTo": "haex_extensions",
|
||||||
|
"columnsFrom": [
|
||||||
|
"extension_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_notifications": {
|
||||||
|
"name": "haex_notifications",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"alt": {
|
||||||
|
"name": "alt",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"name": "image",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"read": {
|
||||||
|
"name": "read",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"name": "source",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"name": "text",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_passwords_group_items": {
|
||||||
|
"name": "haex_passwords_group_items",
|
||||||
|
"columns": {
|
||||||
|
"group_id": {
|
||||||
|
"name": "group_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"item_id": {
|
||||||
|
"name": "item_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"haex_passwords_group_items_group_id_haex_passwords_groups_id_fk": {
|
||||||
|
"name": "haex_passwords_group_items_group_id_haex_passwords_groups_id_fk",
|
||||||
|
"tableFrom": "haex_passwords_group_items",
|
||||||
|
"tableTo": "haex_passwords_groups",
|
||||||
|
"columnsFrom": [
|
||||||
|
"group_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"haex_passwords_group_items_item_id_haex_passwords_item_details_id_fk": {
|
||||||
|
"name": "haex_passwords_group_items_item_id_haex_passwords_item_details_id_fk",
|
||||||
|
"tableFrom": "haex_passwords_group_items",
|
||||||
|
"tableTo": "haex_passwords_item_details",
|
||||||
|
"columnsFrom": [
|
||||||
|
"item_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"haex_passwords_group_items_item_id_group_id_pk": {
|
||||||
|
"columns": [
|
||||||
|
"item_id",
|
||||||
|
"group_id"
|
||||||
|
],
|
||||||
|
"name": "haex_passwords_group_items_item_id_group_id_pk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_passwords_groups": {
|
||||||
|
"name": "haex_passwords_groups",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"name": "order",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"name": "color",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"parent_id": {
|
||||||
|
"name": "parent_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(CURRENT_TIMESTAMP)"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"haex_passwords_groups_parent_id_haex_passwords_groups_id_fk": {
|
||||||
|
"name": "haex_passwords_groups_parent_id_haex_passwords_groups_id_fk",
|
||||||
|
"tableFrom": "haex_passwords_groups",
|
||||||
|
"tableTo": "haex_passwords_groups",
|
||||||
|
"columnsFrom": [
|
||||||
|
"parent_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_passwords_item_details": {
|
||||||
|
"name": "haex_passwords_item_details",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"name": "password",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"note": {
|
||||||
|
"name": "note",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "icon",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"name": "tags",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"name": "url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(CURRENT_TIMESTAMP)"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_passwords_item_history": {
|
||||||
|
"name": "haex_passwords_item_history",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"item_id": {
|
||||||
|
"name": "item_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"changed_property": {
|
||||||
|
"name": "changed_property",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"old_value": {
|
||||||
|
"name": "old_value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"new_value": {
|
||||||
|
"name": "new_value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(CURRENT_TIMESTAMP)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"haex_passwords_item_history_item_id_haex_passwords_item_details_id_fk": {
|
||||||
|
"name": "haex_passwords_item_history_item_id_haex_passwords_item_details_id_fk",
|
||||||
|
"tableFrom": "haex_passwords_item_history",
|
||||||
|
"tableTo": "haex_passwords_item_details",
|
||||||
|
"columnsFrom": [
|
||||||
|
"item_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_passwords_item_key_values": {
|
||||||
|
"name": "haex_passwords_item_key_values",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"item_id": {
|
||||||
|
"name": "item_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"haex_passwords_item_key_values_item_id_haex_passwords_item_details_id_fk": {
|
||||||
|
"name": "haex_passwords_item_key_values_item_id_haex_passwords_item_details_id_fk",
|
||||||
|
"tableFrom": "haex_passwords_item_key_values",
|
||||||
|
"tableTo": "haex_passwords_item_details",
|
||||||
|
"columnsFrom": [
|
||||||
|
"item_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
},
|
||||||
|
"haex_settings": {
|
||||||
|
"name": "haex_settings",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {
|
||||||
|
"\"haex_passwords_items\"": "\"haex_passwords_item_details\"",
|
||||||
|
"\"haex_passwords_items_key_values\"": "\"haex_passwords_item_key_values\""
|
||||||
|
},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -57,6 +57,13 @@
|
|||||||
"when": 1749244165094,
|
"when": 1749244165094,
|
||||||
"tag": "0007_daffy_tusk",
|
"tag": "0007_daffy_tusk",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 8,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1749727958231,
|
||||||
|
"tag": "0008_faulty_mercury",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -69,29 +69,34 @@ export const haexNotifications = sqliteTable('haex_notifications', {
|
|||||||
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
export type InsertHaexNotifications = typeof haexNotifications.$inferInsert
|
||||||
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
export type SelectHaexNotifications = typeof haexNotifications.$inferSelect
|
||||||
|
|
||||||
export const haexPasswordsItems = sqliteTable('haex_passwords_items', {
|
export const haexPasswordsItemDetails = sqliteTable(
|
||||||
id: text().primaryKey(),
|
'haex_passwords_item_details',
|
||||||
title: text(),
|
{
|
||||||
username: text(),
|
id: text().primaryKey(),
|
||||||
password: text(),
|
title: text(),
|
||||||
note: text(),
|
username: text(),
|
||||||
icon: text(),
|
password: text(),
|
||||||
tags: text(),
|
note: text(),
|
||||||
url: text(),
|
icon: text(),
|
||||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
tags: text(),
|
||||||
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
url: text(),
|
||||||
() => new Date(),
|
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||||
),
|
updateAt: integer('updated_at', { mode: 'timestamp' }).$onUpdate(
|
||||||
})
|
() => new Date(),
|
||||||
export type InsertHaexPasswordsItems = typeof haexPasswordsItems.$inferInsert
|
),
|
||||||
export type SelectHaexPasswordsItems = typeof haexPasswordsItems.$inferSelect
|
},
|
||||||
|
)
|
||||||
|
export type InsertHaexPasswordsItemDetails =
|
||||||
|
typeof haexPasswordsItemDetails.$inferInsert
|
||||||
|
export type SelectHaexPasswordsItemDetails =
|
||||||
|
typeof haexPasswordsItemDetails.$inferSelect
|
||||||
|
|
||||||
export const haexPasswordsItemsKeyValues = sqliteTable(
|
export const haexPasswordsItemKeyValues = sqliteTable(
|
||||||
'haex_passwords_items_key_values',
|
'haex_passwords_item_key_values',
|
||||||
{
|
{
|
||||||
id: text().primaryKey(),
|
id: text().primaryKey(),
|
||||||
itemId: text('item_id').references(
|
itemId: text('item_id').references(
|
||||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
(): AnySQLiteColumn => haexPasswordsItemDetails.id,
|
||||||
),
|
),
|
||||||
key: text(),
|
key: text(),
|
||||||
value: text(),
|
value: text(),
|
||||||
@ -100,25 +105,29 @@ export const haexPasswordsItemsKeyValues = sqliteTable(
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
export type InsertHaexPasswordsItemsKeyValues =
|
export type InserthaexPasswordsItemKeyValues =
|
||||||
typeof haexPasswordsItemsKeyValues.$inferInsert
|
typeof haexPasswordsItemKeyValues.$inferInsert
|
||||||
export type SelectHaexPasswordsItemsKeyValues =
|
export type SelectHaexPasswordsItemKeyValues =
|
||||||
typeof haexPasswordsItemsKeyValues.$inferSelect
|
typeof haexPasswordsItemKeyValues.$inferSelect
|
||||||
|
|
||||||
export const haexPasswordsItemHistory = sqliteTable(
|
export const haexPasswordsItemHistory = sqliteTable(
|
||||||
'haex_passwords_item_history',
|
'haex_passwords_item_history',
|
||||||
{
|
{
|
||||||
id: text().primaryKey(),
|
id: text().primaryKey(),
|
||||||
itemId: text('item_id').references(
|
itemId: text('item_id').references(
|
||||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
(): AnySQLiteColumn => haexPasswordsItemDetails.id,
|
||||||
),
|
),
|
||||||
changedProperty:
|
changedProperty:
|
||||||
text('changed_property').$type<keyof typeof haexPasswordsItems>(),
|
text('changed_property').$type<keyof typeof haexPasswordsItemDetails>(),
|
||||||
oldValue: text('old_value'),
|
oldValue: text('old_value'),
|
||||||
newValue: text('new_value'),
|
newValue: text('new_value'),
|
||||||
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
createdAt: text('created_at').default(sql`(CURRENT_TIMESTAMP)`),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
export type InserthaexPasswordsItemHistory =
|
||||||
|
typeof haexPasswordsItemHistory.$inferInsert
|
||||||
|
export type SelectHaexPasswordsItemHistory =
|
||||||
|
typeof haexPasswordsItemHistory.$inferSelect
|
||||||
|
|
||||||
export const haexPasswordsGroups = sqliteTable('haex_passwords_groups', {
|
export const haexPasswordsGroups = sqliteTable('haex_passwords_groups', {
|
||||||
id: text().primaryKey(),
|
id: text().primaryKey(),
|
||||||
@ -145,7 +154,7 @@ export const haexPasswordsGroupItems = sqliteTable(
|
|||||||
(): AnySQLiteColumn => haexPasswordsGroups.id,
|
(): AnySQLiteColumn => haexPasswordsGroups.id,
|
||||||
),
|
),
|
||||||
itemId: text('item_id').references(
|
itemId: text('item_id').references(
|
||||||
(): AnySQLiteColumn => haexPasswordsItems.id,
|
(): AnySQLiteColumn => haexPasswordsItemDetails.id,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
(table) => [primaryKey({ columns: [table.itemId, table.groupId] })],
|
(table) => [primaryKey({ columns: [table.itemId, table.groupId] })],
|
||||||
|
|||||||
Binary file not shown.
10
src/app.vue
10
src/app.vue
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NuxtLayout :data-theme="currentTheme.value">
|
<NuxtLayout>
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
<NuxtSnackbar />
|
<NuxtSnackbar />
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
@ -8,7 +8,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { currentTheme } = storeToRefs(useUiStore())
|
const { currentThemeValue } = storeToRefs(useUiStore())
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
htmlAttrs: {
|
||||||
|
'data-theme': currentThemeValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@ -8,4 +8,4 @@
|
|||||||
themes: all;
|
themes: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @source "../../../node_modules/flyonui/flyonui.js"; */
|
@source "../../../node_modules/flyonui/flyonui.js";
|
||||||
|
|||||||
BIN
src/assets/images/horn-fragend.png
Normal file
BIN
src/assets/images/horn-fragend.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<fieldset class="join w-full">
|
|
||||||
<slot name="prepend" />
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <div class="">
|
|
||||||
-->
|
|
||||||
<HaexButton
|
|
||||||
v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
|
||||||
@click="copy(`${input}`)">
|
|
||||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
|
||||||
</Haexbutton>
|
|
||||||
|
|
||||||
<!-- <div class="">
|
|
||||||
<input :id :name="name ?? id" :placeholder="placeholder || label" :type :autofocus class="" v-bind="$attrs"
|
|
||||||
v-model="input" ref="inputRef" :readonly="read_only" />
|
|
||||||
<label class="floating-label" :for="id">{{ label }}</label>
|
|
||||||
</div> -->
|
|
||||||
<label class="floating-label input join-item">
|
|
||||||
<Icon v-if="iconPrepend" :name="iconPrepend" class="my-auto size-6" />
|
|
||||||
<span>Your Email</span>
|
|
||||||
<input type="text" placeholder="mail@site.com" class=" join-item " >
|
|
||||||
<Icon v-if="iconAppend" :name="iconAppend" class="my-auto shrink-0" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- <Icon v-if="iconAppend" :name="iconAppend" class="my-auto shrink-0" />
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<slot name="append" class="h-auto" />
|
|
||||||
|
|
||||||
<HaexButton
|
|
||||||
v-if="withCopyButton" class="btn-outline btn-accent btn-square join-item h-auto"
|
|
||||||
@click="copy(`${input}`)">
|
|
||||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
|
||||||
</Haexbutton>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<span v-show="errors" class="flex flex-col px-2 pt-0.5">
|
|
||||||
<span v-for="error in errors" class="label-text-alt text-error">
|
|
||||||
{{ error }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="relative w-full max-w-sm items-center">
|
|
||||||
<span class="absolute start-0 inset-y-0 flex items-center justify-center px-2">
|
|
||||||
<Icon v-if="iconPrepend" :name="iconPrepend" class="size-6" />
|
|
||||||
<button>aa</button>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<Input id="search" type="text" placeholder="Search..." :class="{ 'pl-10': iconPrepend, 'pr-10': iconAppend }" />
|
|
||||||
|
|
||||||
<span class="absolute end-0 inset-y-0 flex items-center justify-center px-2">
|
|
||||||
<Icon v-if="iconAppend" :name="iconAppend" class="size-6" />
|
|
||||||
</span>
|
|
||||||
</div> -->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { HaexButton } from '#components';
|
|
||||||
import type { ZodSchema } from 'zod';
|
|
||||||
|
|
||||||
const id = useId()
|
|
||||||
|
|
||||||
const props = defineProps<{ iconAppend?: string, iconPrepend?: string, placeholder?: string, type?: string, label?: string, withCopyButton?: boolean, rules?: ZodSchema, read_only?: boolean, autofocus?: boolean, checkInput?: boolean, name?: string }>()
|
|
||||||
|
|
||||||
const inputRef = useTemplateRef('inputRef')
|
|
||||||
|
|
||||||
const input = defineModel<string | number | undefined | null>({
|
|
||||||
default: '',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (props.autofocus && inputRef.value) inputRef.value.focus()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(input, () => checkInput())
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.checkInput,
|
|
||||||
() => {
|
|
||||||
checkInput()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const emit = defineEmits(['error'])
|
|
||||||
|
|
||||||
const errors = defineModel<string[] | undefined>('errors')
|
|
||||||
const checkInput = () => {
|
|
||||||
if (props.rules) {
|
|
||||||
const result = props.rules.safeParse(input.value)
|
|
||||||
//console.log('check result', result.error, props.rules);
|
|
||||||
if (!result.success) {
|
|
||||||
errors.value = result.error.errors.map((error) => error.message)
|
|
||||||
emit('error', errors.value)
|
|
||||||
} else {
|
|
||||||
errors.value = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { copy, copied } = useClipboard()
|
|
||||||
</script>
|
|
||||||
90
src/components/haex/menu/notifications.vue
Normal file
90
src/components/haex/menu/notifications.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="dropdown relative inline-flex [--auto-close:inside] [--offset:18] [--placement:bottom]"
|
||||||
|
>
|
||||||
|
<UiTooltip :tooltip="t('notifications.label')">
|
||||||
|
<button
|
||||||
|
id="dropdown-scrollable"
|
||||||
|
type="button"
|
||||||
|
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Dropdown"
|
||||||
|
>
|
||||||
|
<div class="indicator">
|
||||||
|
<span
|
||||||
|
v-show="notifications.length"
|
||||||
|
class="indicator-item bg-error size-2 rounded-full text-sm"
|
||||||
|
/>
|
||||||
|
<span class="icon-[tabler--bell] text-base-content size-[1.375rem]" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</UiTooltip>
|
||||||
|
<div
|
||||||
|
class="dropdown-menu dropdown-open:opacity-100 hidden w-full max-w-96 shadow"
|
||||||
|
role="menu"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
aria-labelledby="dropdown-scrollable"
|
||||||
|
>
|
||||||
|
<div class="dropdown-header justify-center">
|
||||||
|
<h6 class="text-base-content text-base">
|
||||||
|
{{ t('notifications.label') }}
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="notification in notifications"
|
||||||
|
:key="notification.id"
|
||||||
|
class="dropdown-item"
|
||||||
|
>
|
||||||
|
<div class="avatar">
|
||||||
|
<div class="w-10 rounded-full">
|
||||||
|
<img
|
||||||
|
v-if="notification.image"
|
||||||
|
:src="notification.image"
|
||||||
|
:alt="notification.alt ?? 'notification avatar'"
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
v-else-if="notification.icon"
|
||||||
|
:name="notification.icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-60">
|
||||||
|
<h6 class="truncate text-base">
|
||||||
|
{{ notification.title }}
|
||||||
|
</h6>
|
||||||
|
<small class="text-base-content/50 truncate">
|
||||||
|
{{ notification.text }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NuxtLinkLocale
|
||||||
|
:to="{ name: 'notifications' }"
|
||||||
|
class="dropdown-footer justify-center gap-1 hover:bg-base-content/10"
|
||||||
|
>
|
||||||
|
<span class="icon-[tabler--eye] size-4" />
|
||||||
|
{{ t('notifications.view_all') }}
|
||||||
|
</NuxtLinkLocale>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { notifications } = storeToRefs(useNotificationStore())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
notifications:
|
||||||
|
label: Benachrichtigungen
|
||||||
|
view_all: Alle ansehen
|
||||||
|
en:
|
||||||
|
notifications:
|
||||||
|
label: Notifications
|
||||||
|
view_all: View all
|
||||||
|
</i18n>
|
||||||
107
src/components/haex/pass/card/group.vue
Normal file
107
src/components/haex/pass/card/group.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<HaexPassCard
|
||||||
|
:title
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-4 w-full p-4">
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<UiInput
|
||||||
|
v-show="!read_only"
|
||||||
|
v-model.trim="passwordGroup.name"
|
||||||
|
:label="t('group.name')"
|
||||||
|
:placeholder="t('group.name')"
|
||||||
|
:with-copy-button="read_only"
|
||||||
|
:read_only
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiInput
|
||||||
|
v-show="!read_only || passwordGroup.description?.length"
|
||||||
|
v-model.trim="passwordGroup.description"
|
||||||
|
:read_only
|
||||||
|
:label="t('group.description')"
|
||||||
|
:placeholder="t('group.description')"
|
||||||
|
:with-copy-button="read_only"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiSelectColor
|
||||||
|
v-model="passwordGroup.color"
|
||||||
|
:read_only
|
||||||
|
:label="t('group.color')"
|
||||||
|
:placeholder="t('group.color')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiSelectIcon
|
||||||
|
v-model="passwordGroup.icon"
|
||||||
|
:read_only
|
||||||
|
:label="t('group.icon')"
|
||||||
|
:placeholder="t('group.icon')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<slot name="footer" />
|
||||||
|
</HaexPassCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
|
||||||
|
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const showConfirmation = ref(false)
|
||||||
|
const passwordGroup = defineModel<SelectHaexPasswordsGroups>({ required: true })
|
||||||
|
const read_only = defineModel<boolean>('read_only')
|
||||||
|
const props = defineProps<{
|
||||||
|
originally: SelectHaexPasswordsGroups
|
||||||
|
title: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
submit: [to?: RouteLocationNormalizedLoadedGeneric]
|
||||||
|
close: [void]
|
||||||
|
back: [void]
|
||||||
|
reject: [to?: RouteLocationNormalizedLoadedGeneric]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const hasChanges = computed(() => {
|
||||||
|
console.log('group has changes', props.originally, passwordGroup.value)
|
||||||
|
if (!props.originally) {
|
||||||
|
if (
|
||||||
|
passwordGroup.value.color?.length ||
|
||||||
|
passwordGroup.value.description?.length ||
|
||||||
|
passwordGroup.value.icon?.length ||
|
||||||
|
passwordGroup.value.name?.length
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
JSON.stringify(props.originally) !== JSON.stringify(passwordGroup.value)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
/* if (props.originally) passwordGroup.value = { ...props.originally };
|
||||||
|
emit('close'); */
|
||||||
|
console.log('close group card')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
group:
|
||||||
|
name: Name
|
||||||
|
description: Beschreibung
|
||||||
|
icon: Icon
|
||||||
|
color: Farbe
|
||||||
|
|
||||||
|
en:
|
||||||
|
group:
|
||||||
|
name: Name
|
||||||
|
description: Description
|
||||||
|
icon: Icon
|
||||||
|
color: Color
|
||||||
|
</i18n>
|
||||||
12
src/components/haex/pass/card/index.vue
Normal file
12
src/components/haex/pass/card/index.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<UiCard
|
||||||
|
:title
|
||||||
|
:icon
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</UiCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ title: string; icon?: string }>()
|
||||||
|
</script>
|
||||||
@ -1,190 +0,0 @@
|
|||||||
<template>
|
|
||||||
<VaultCardEdit
|
|
||||||
v-if="vaultEntry.details" v-model:read_only="read_only" :color="currentGroup?.color || 'text-base-content'"
|
|
||||||
:has-changes="hasChanges" :icon="vaultEntry.details?.icon || icon || 'mdi:key-outline'"
|
|
||||||
:title="vaultEntry.details?.title ?? ''" @back="$emit('back')" @close="$emit('close')"
|
|
||||||
@reject="(to) => $emit('reject', to)" @submit="(to) => $emit('submit', to)">
|
|
||||||
<div class="h-full relative overflow-hidden">
|
|
||||||
<nav
|
|
||||||
aria-label="Tabs Vault Entry" aria-orientation="horizontal"
|
|
||||||
class="tabs tabs-bordered w-full transition-all duration-700 sticky top-0 z-10 bg-base-200" role="tablist">
|
|
||||||
<button
|
|
||||||
:id="id.details" aria-controls="vaultDetailsId" aria-selected="true"
|
|
||||||
class="tab active-tab:tab-active active w-full" data-tab="#vaultDetailsId" role="tab" type="button">
|
|
||||||
<Icon name="material-symbols:key-outline" class="me-2" />
|
|
||||||
<span class="hidden sm:block">
|
|
||||||
{{ t('tab.details') }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:id="id.keyValue" aria-controls="tabs-basic-2" aria-selected="false"
|
|
||||||
class="tab active-tab:tab-active w-full" data-tab="#tabs-basic-2" role="tab" type="button">
|
|
||||||
<Icon name="fluent:group-list-20-filled" class="me-2" />
|
|
||||||
<span class="hidden sm:block">
|
|
||||||
{{ t('tab.keyValue') }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:id="id.history" aria-controls="tabs-basic-3" aria-selected="false"
|
|
||||||
class="tab active-tab:tab-active w-full" data-tab="#tabs-basic-3" role="tab" type="button">
|
|
||||||
<Icon name="material-symbols:history" class="me-2" />
|
|
||||||
<span class="hidden sm:block">
|
|
||||||
{{ t('tab.history') }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="h-full pb-8">
|
|
||||||
<div id="vaultDetailsId" role="tabpanel" :aria-labelledby="id.details" class="h-full">
|
|
||||||
<VaultEntryDetails v-if="vaultEntry.details" v-model="vaultEntry.details" :with-copy-button :read_only/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tabs-basic-2" class="hidden" role="tabpanel" :aria-labelledby="id.keyValue">
|
|
||||||
{{ originally }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tabs-basic-3" class="hidden h-full" role="tabpanel" :aria-labelledby="id.history">
|
|
||||||
<VaultEntryHistory v-if="vaultEntry.history" :history="vaultEntry.history" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VaultCardEdit>
|
|
||||||
<!-- <VaultModalSaveChanges
|
|
||||||
v-model="showConfirmation"
|
|
||||||
@reject="onReject"
|
|
||||||
@submit="onSubmit"
|
|
||||||
/> -->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const vaultEntry = defineModel<IVaultEntryComplete>({ required: true });
|
|
||||||
|
|
||||||
|
|
||||||
const { currentGroup } = storeToRefs(useVaultGroupStore());
|
|
||||||
|
|
||||||
/* watch(
|
|
||||||
() => vaultEntry.value.details,
|
|
||||||
() => {
|
|
||||||
header.value.text = vaultEntry.value.details?.title;
|
|
||||||
header.value.icon =
|
|
||||||
vaultEntry.value.details?.icon || currentGroup.value?.icon;
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
); */
|
|
||||||
|
|
||||||
const id = reactive({
|
|
||||||
details: useId(),
|
|
||||||
keyValue: useId(),
|
|
||||||
history: useId(),
|
|
||||||
content: {},
|
|
||||||
});
|
|
||||||
|
|
||||||
const read_only = defineModel<boolean>('read_only', { default: false });
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
icon: String,
|
|
||||||
originally: Object as PropType<IVaultEntryComplete>,
|
|
||||||
title: String,
|
|
||||||
withCopyButton: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
submit: [to?: RouteLocationNormalizedLoadedGeneric];
|
|
||||||
close: [void];
|
|
||||||
back: [void];
|
|
||||||
reject: [to?: RouteLocationNormalizedLoadedGeneric];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const showConfirmation = ref(false);
|
|
||||||
|
|
||||||
const hasChanges = computed(() => {
|
|
||||||
if (!props.originally?.details) {
|
|
||||||
if (
|
|
||||||
vaultEntry.value.details?.note?.length ||
|
|
||||||
vaultEntry.value.details?.password?.length ||
|
|
||||||
vaultEntry.value.details?.tags?.length ||
|
|
||||||
vaultEntry.value.details?.title?.length ||
|
|
||||||
vaultEntry.value.details?.url?.length ||
|
|
||||||
vaultEntry.value.details?.urlAliases?.length ||
|
|
||||||
vaultEntry.value.details?.username?.length
|
|
||||||
) {
|
|
||||||
console.log('has changes', props.originally, vaultEntry.value);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
JSON.stringify(props.originally.details) !==
|
|
||||||
JSON.stringify(vaultEntry.value.details)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const to = ref<RouteLocationNormalizedLoadedGeneric>();
|
|
||||||
|
|
||||||
const isSaved = ref(false);
|
|
||||||
const isRejected = ref(false);
|
|
||||||
|
|
||||||
const onSubmit = () => {
|
|
||||||
console.log('entry onSubmit');
|
|
||||||
showConfirmation.value = false;
|
|
||||||
isSaved.value = true;
|
|
||||||
emit('submit', to.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onReject = () => {
|
|
||||||
console.log('entry onReject');
|
|
||||||
showConfirmation.value = false;
|
|
||||||
isRejected.value = true;
|
|
||||||
emit('reject', to.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBack = () => {
|
|
||||||
console.log('entry onBack', read_only.value);
|
|
||||||
if (hasChanges.value) {
|
|
||||||
showConfirmation.value = true;
|
|
||||||
} else {
|
|
||||||
emit('back');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* onBeforeRouteLeave((_to, _from, next) => {
|
|
||||||
console.log('check before leave', _to, _from);
|
|
||||||
to.value = _to;
|
|
||||||
if (isSaved.value || isRejected.value) {
|
|
||||||
isSaved.value = false;
|
|
||||||
isRejected.value = false;
|
|
||||||
next();
|
|
||||||
} else if (hasChanges.value) {
|
|
||||||
showConfirmation.value = true;
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}); */
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<i18n lang="json">{
|
|
||||||
"de": {
|
|
||||||
"create": "Anlegen",
|
|
||||||
"abort": "Abbrechen",
|
|
||||||
"tab": {
|
|
||||||
"details": "Details",
|
|
||||||
"keyValue": "Extra",
|
|
||||||
"history": "Verlauf"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"create": "Create",
|
|
||||||
"abort": "Abort",
|
|
||||||
"tab": {
|
|
||||||
"details": "Details",
|
|
||||||
"keyValue": "Extra",
|
|
||||||
"history": "History"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}</i18n>
|
|
||||||
45
src/components/haex/pass/group/breadcrumbs.vue
Normal file
45
src/components/haex/pass/group/breadcrumbs.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<NuxtLinkLocale :to="{ name: 'passwordGroupItems' }">
|
||||||
|
<Icon
|
||||||
|
name="mdi:safe"
|
||||||
|
size="24"
|
||||||
|
/>
|
||||||
|
</NuxtLinkLocale>
|
||||||
|
</li>
|
||||||
|
<template v-for="item in items">
|
||||||
|
<li class="breadcrumbs-separator rtl:rotate-180">
|
||||||
|
<Icon name="tabler:chevron-right" />
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<NuxtLinkLocale
|
||||||
|
:to="{ name: 'passwordGroupItems', params: { groupId: item.id } }"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</NuxtLinkLocale>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<li class="ml-2">
|
||||||
|
<NuxtLinkLocale
|
||||||
|
:to="{
|
||||||
|
name: 'passwordGroupEdit',
|
||||||
|
params: { groupId: lastGroup?.id },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:pencil" />
|
||||||
|
</NuxtLinkLocale>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
const groups = defineProps<{ items: SelectHaexPasswordsGroups[] }>()
|
||||||
|
|
||||||
|
const lastGroup = computed(() => groups.items.at(-1))
|
||||||
|
</script>
|
||||||
@ -1 +1,98 @@
|
|||||||
<template></template>
|
<template>
|
||||||
|
<UiCard
|
||||||
|
v-if="modelValue"
|
||||||
|
:title="mode === 'create' ? t('title.create') : t('title.edit')"
|
||||||
|
icon="mdi:folder-plus-outline"
|
||||||
|
@close="$emit('close')"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
class="flex flex-col gap-4 w-full p-4"
|
||||||
|
@submit.prevent="$emit('submit')"
|
||||||
|
>
|
||||||
|
<UiInput
|
||||||
|
:check-input="check"
|
||||||
|
:label="t('name')"
|
||||||
|
:placeholder="t('name')"
|
||||||
|
autofocus
|
||||||
|
v-model="modelValue.name"
|
||||||
|
ref="nameRef"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiInput
|
||||||
|
v-model="modelValue.description"
|
||||||
|
:check-input="check"
|
||||||
|
:label="t('description')"
|
||||||
|
:placeholder="t('description')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<UiSelectIcon
|
||||||
|
v-model="modelValue.icon"
|
||||||
|
default-icon="mdi:folder-outline"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiSelectColor v-model="modelValue.color" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap justify-end gap-4">
|
||||||
|
<UiButton
|
||||||
|
class="btn-error btn-outline flex-1"
|
||||||
|
@click="$emit('close')"
|
||||||
|
>
|
||||||
|
{{ t('abort') }}
|
||||||
|
<Icon name="mdi:close" />
|
||||||
|
</UiButton>
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
class="btn-primary flex-1"
|
||||||
|
@click="$emit('submit')"
|
||||||
|
>
|
||||||
|
{{ mode === 'create' ? t('create') : t('save') }}
|
||||||
|
<Icon name="mdi:check" />
|
||||||
|
</UiButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</UiCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
defineModel<SelectHaexPasswordsGroups | null>()
|
||||||
|
defineEmits(['close', 'submit', 'back'])
|
||||||
|
defineProps<{ mode: 'create' | 'edit' }>()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const check = ref<boolean>(false)
|
||||||
|
|
||||||
|
const nameRef = useTemplateRef('nameRef')
|
||||||
|
onStartTyping(() => {
|
||||||
|
nameRef.value?.inputRef?.focus()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
name: Name
|
||||||
|
description: Beschreibung
|
||||||
|
icon: Icon
|
||||||
|
color: Farbe
|
||||||
|
create: Erstellen
|
||||||
|
save: Speichern
|
||||||
|
abort: Abbrechen
|
||||||
|
title:
|
||||||
|
create: Gruppe erstellen
|
||||||
|
edit: Gruppe ändern
|
||||||
|
|
||||||
|
en:
|
||||||
|
name: Name
|
||||||
|
description: Description
|
||||||
|
icon: Icon
|
||||||
|
color: Color
|
||||||
|
create: Create
|
||||||
|
save: Save
|
||||||
|
abort: Abort
|
||||||
|
title:
|
||||||
|
create: Create group
|
||||||
|
edit: Edit group
|
||||||
|
</i18n>
|
||||||
|
|||||||
119
src/components/haex/pass/item/details.vue
Normal file
119
src/components/haex/pass/item/details.vue
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full overflow-scroll">
|
||||||
|
<div class="flex flex-col gap-4 w-full p-4">
|
||||||
|
<UiInput
|
||||||
|
v-show="!read_only || itemDetails.title"
|
||||||
|
:check-input="check"
|
||||||
|
:label="t('item.title')"
|
||||||
|
:placeholder="t('item.title')"
|
||||||
|
:read_only
|
||||||
|
:with-copy-button
|
||||||
|
autofocus
|
||||||
|
ref="titleRef"
|
||||||
|
v-model.trim="itemDetails.title"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiInput
|
||||||
|
v-show="!read_only || itemDetails.username"
|
||||||
|
:check-input="check"
|
||||||
|
:label="t('item.username')"
|
||||||
|
:placeholder="t('item.username')"
|
||||||
|
:with-copy-button
|
||||||
|
:read_only
|
||||||
|
v-model.trim="itemDetails.username"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiInputPassword
|
||||||
|
v-show="!read_only || itemDetails.password"
|
||||||
|
:check-input="check"
|
||||||
|
:read_only
|
||||||
|
:with-copy-button
|
||||||
|
v-model.trim="itemDetails.password"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<UiDialogPasswordGenerator
|
||||||
|
v-if="!read_only"
|
||||||
|
class="join-item"
|
||||||
|
:password="itemDetails.password"
|
||||||
|
v-model="preventClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UiInputPassword>
|
||||||
|
|
||||||
|
<UiInputUrl
|
||||||
|
v-show="!read_only || itemDetails.url"
|
||||||
|
:label="t('item.url')"
|
||||||
|
:placeholder="t('item.url')"
|
||||||
|
:read_only
|
||||||
|
:with-copy-button
|
||||||
|
v-model="itemDetails.url"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiSelectIcon
|
||||||
|
v-show="!read_only"
|
||||||
|
:default-icon="defaultIcon || 'mdi:key-outline'"
|
||||||
|
:read_only
|
||||||
|
v-model="itemDetails.icon"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UiTextarea
|
||||||
|
v-show="!read_only || itemDetails.note"
|
||||||
|
v-model="itemDetails.note"
|
||||||
|
:label="t('item.note')"
|
||||||
|
:placeholder="t('item.note')"
|
||||||
|
:read_only
|
||||||
|
:with-copy-button
|
||||||
|
@keyup.enter.stop
|
||||||
|
class="h-52"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectHaexPasswordsItemDetails } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
defaultIcon?: string | null
|
||||||
|
read_only?: boolean
|
||||||
|
withCopyButton?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const itemDetails = defineModel<SelectHaexPasswordsItemDetails>({
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const preventClose = defineModel<boolean>('preventClose')
|
||||||
|
|
||||||
|
const check = defineModel<boolean>('check-input', { default: false })
|
||||||
|
|
||||||
|
onKeyStroke('escape', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
})
|
||||||
|
|
||||||
|
const titleRef = useTemplateRef('titleRef')
|
||||||
|
onStartTyping(() => {
|
||||||
|
titleRef.value?.inputRef?.focus()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
item:
|
||||||
|
title: Titel
|
||||||
|
username: Nutzername
|
||||||
|
password: Passwort
|
||||||
|
url: Url
|
||||||
|
note: Notiz
|
||||||
|
|
||||||
|
en:
|
||||||
|
item:
|
||||||
|
title: Title
|
||||||
|
username: Username
|
||||||
|
password: Password
|
||||||
|
url: Url
|
||||||
|
note: Note
|
||||||
|
</i18n>
|
||||||
118
src/components/haex/pass/item/history.vue
Normal file
118
src/components/haex/pass/item/history.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full overflow-scroll flex">
|
||||||
|
{{ _history }}
|
||||||
|
<UiList v-show="_history.length">
|
||||||
|
<!-- <UiListButton v-for="item in _history">
|
||||||
|
<div
|
||||||
|
class="flex items-start bg-slate-100 gap-x-2 w-full h-20 overflow-clip"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col justify-between h-full py-2">
|
||||||
|
<h6 class="text-sm whitespace-nowrap bg-orange-200">
|
||||||
|
vorheriger {{ item.changedProperty }}
|
||||||
|
</h6>
|
||||||
|
<UiInput
|
||||||
|
:model-value="item.oldValue"
|
||||||
|
with-copy-button
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sm:flex flex-col justify-between h-full py-2 hidden">
|
||||||
|
<h6 class="text-sm">neuer Wert</h6>
|
||||||
|
<UiInput
|
||||||
|
:model-value="item.newValue"
|
||||||
|
with-copy-button
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col justify-between h-full py-2">
|
||||||
|
<h6 class="text-sm md:text-base bg-orange-200">geändert_am</h6>
|
||||||
|
<span class="bg-red-100 py-1 md:py-2">
|
||||||
|
{{ item.createdAt }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UiListButton>
|
||||||
|
-->
|
||||||
|
</UiList>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-show="!_history.length"
|
||||||
|
class="content-center w-full text-center"
|
||||||
|
>
|
||||||
|
{{ t('noHistory') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <UiTable
|
||||||
|
v-if="history?.length"
|
||||||
|
:headers
|
||||||
|
:items="_history"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<template #column-oldValue="{ item }: { item: string }">
|
||||||
|
<UiInput
|
||||||
|
:model-value="item"
|
||||||
|
with-copy-button
|
||||||
|
class="min-w-24"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UiTable> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
SelectHaexPasswordsGroupItems,
|
||||||
|
SelectHaexPasswordsItemDetails,
|
||||||
|
SelectHaexPasswordsItemHistory,
|
||||||
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
const history = defineModel<SelectHaexPasswordsItemHistory[]>()
|
||||||
|
|
||||||
|
const _history = computed(
|
||||||
|
() =>
|
||||||
|
history.value?.map((change) => ({
|
||||||
|
changedProperty: t(change.changedProperty!),
|
||||||
|
createdAt: new Date(change.createdAt!).toLocaleDateString(),
|
||||||
|
newValue: change.newValue,
|
||||||
|
oldValue: change.oldValue,
|
||||||
|
})) ?? [],
|
||||||
|
)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
interface ITableHeader {
|
||||||
|
label?: string
|
||||||
|
'item-value': string
|
||||||
|
}
|
||||||
|
const headers: ITableHeader[] = [
|
||||||
|
{ 'item-value': 'changedProperty', label: t('changedProperty') },
|
||||||
|
{ 'item-value': 'oldValue', label: t('oldValue') },
|
||||||
|
{ 'item-value': 'newValue', label: t('newValue') },
|
||||||
|
{ 'item-value': 'createdAt', label: t('createdAt') },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="json">
|
||||||
|
{
|
||||||
|
"de": {
|
||||||
|
"noHistory": "Eintrag wurde bisher nicht geändert",
|
||||||
|
"changedProperty": "Änderung",
|
||||||
|
"createdAt": "geändert am",
|
||||||
|
"newValue": "neuer Wert",
|
||||||
|
"oldValue": "alter Wert",
|
||||||
|
"password": "Passwort",
|
||||||
|
"title": "Titel",
|
||||||
|
"url": "Url",
|
||||||
|
"username": "Nutzername"
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
"noHistory": "No changes so far",
|
||||||
|
"changedProperty": "Changes",
|
||||||
|
"createdAt": "changed at",
|
||||||
|
"newValue": "new Value",
|
||||||
|
"oldValue": "old Value",
|
||||||
|
"password": "Password",
|
||||||
|
"title": "Title",
|
||||||
|
"url": "Url",
|
||||||
|
"username": "Username"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
188
src/components/haex/pass/item/index.vue
Normal file
188
src/components/haex/pass/item/index.vue
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<UiCard
|
||||||
|
body-class="rounded overflow-auto px-0 h-full"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="">
|
||||||
|
<nav
|
||||||
|
aria-label="Tabs Password Item"
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
class="tabs tabs-bordered w-full transition-all duration-700 sticky top-0 z-10"
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
:id="id.details"
|
||||||
|
aria-controls="vaultDetailsId"
|
||||||
|
aria-selected="true"
|
||||||
|
class="tab active-tab:tab-active active w-full"
|
||||||
|
data-tab="#vaultDetailsId"
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="material-symbols:key-outline"
|
||||||
|
class="me-2"
|
||||||
|
/>
|
||||||
|
<span class="hidden sm:block">
|
||||||
|
{{ t('tab.details') }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:id="id.keyValue"
|
||||||
|
aria-controls="tabs-basic-2"
|
||||||
|
aria-selected="false"
|
||||||
|
class="tab active-tab:tab-active w-full"
|
||||||
|
data-tab="#tabs-basic-2"
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="fluent:group-list-20-filled"
|
||||||
|
class="me-2"
|
||||||
|
/>
|
||||||
|
<span class="hidden sm:block">
|
||||||
|
{{ t('tab.keyValue') }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:id="id.history"
|
||||||
|
aria-controls="tabs-basic-3"
|
||||||
|
aria-selected="false"
|
||||||
|
class="tab active-tab:tab-active w-full"
|
||||||
|
data-tab="#tabs-basic-3"
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="material-symbols:history"
|
||||||
|
class="me-2"
|
||||||
|
/>
|
||||||
|
<span class="hidden sm:block">
|
||||||
|
{{ t('tab.history') }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="h-full pb-8">
|
||||||
|
<div
|
||||||
|
id="vaultDetailsId"
|
||||||
|
role="tabpanel"
|
||||||
|
class="h-full"
|
||||||
|
:aria-labelledby="id.details"
|
||||||
|
>
|
||||||
|
<HaexPassItemDetails
|
||||||
|
v-if="details"
|
||||||
|
v-model="details"
|
||||||
|
with-copy-button
|
||||||
|
:read_only
|
||||||
|
:defaultIcon
|
||||||
|
v-model:prevent-close="preventClose"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="tabs-basic-2"
|
||||||
|
class="hidden"
|
||||||
|
role="tabpanel"
|
||||||
|
:aria-labelledby="id.keyValue"
|
||||||
|
>
|
||||||
|
<HaexPassItemKeyValue
|
||||||
|
v-if="keyValues"
|
||||||
|
v-model="keyValues"
|
||||||
|
v-model:items-to-add="keyValuesAdd"
|
||||||
|
v-model:items-to-delete="keyValuesDelete"
|
||||||
|
:read_only
|
||||||
|
:item-id="details!.id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="tabs-basic-3"
|
||||||
|
class="hidden h-full"
|
||||||
|
role="tabpanel"
|
||||||
|
:aria-labelledby="id.history"
|
||||||
|
>
|
||||||
|
<!-- <HaexPassItemHistory v-model="itemHistory" /> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UiCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
SelectHaexPasswordsItemDetails,
|
||||||
|
SelectHaexPasswordsItemHistory,
|
||||||
|
SelectHaexPasswordsItemKeyValues,
|
||||||
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
defaultIcon?: string | null
|
||||||
|
history: SelectHaexPasswordsItemHistory[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
close: [void]
|
||||||
|
addKeyValue: [void]
|
||||||
|
removeKeyValue: [string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const read_only = defineModel<boolean>('read_only', { default: false })
|
||||||
|
|
||||||
|
const details = defineModel<SelectHaexPasswordsItemDetails | null>('details', {
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const keyValues = defineModel<SelectHaexPasswordsItemKeyValues[]>('keyValues', {
|
||||||
|
default: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const keyValuesAdd = defineModel<SelectHaexPasswordsItemKeyValues[]>(
|
||||||
|
'keyValuesAdd',
|
||||||
|
{ default: [] },
|
||||||
|
)
|
||||||
|
const keyValuesDelete = defineModel<SelectHaexPasswordsItemKeyValues[]>(
|
||||||
|
'keyValuesDelete',
|
||||||
|
{ default: [] },
|
||||||
|
)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const id = reactive({
|
||||||
|
details: useId(),
|
||||||
|
keyValue: useId(),
|
||||||
|
history: useId(),
|
||||||
|
content: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
const preventClose = ref(false)
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
if (preventClose.value) return
|
||||||
|
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="json">
|
||||||
|
{
|
||||||
|
"de": {
|
||||||
|
"create": "Anlegen",
|
||||||
|
"abort": "Abbrechen",
|
||||||
|
"tab": {
|
||||||
|
"details": "Details",
|
||||||
|
"keyValue": "Extra",
|
||||||
|
"history": "Verlauf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
"create": "Create",
|
||||||
|
"abort": "Abort",
|
||||||
|
"tab": {
|
||||||
|
"details": "Details",
|
||||||
|
"keyValue": "Extra",
|
||||||
|
"history": "History"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
128
src/components/haex/pass/item/keyValue.vue
Normal file
128
src/components/haex/pass/item/keyValue.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<UiList
|
||||||
|
v-if="items.length || itemsToAdd.length"
|
||||||
|
class="flex-1"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="item in [...items, ...itemsToAdd]"
|
||||||
|
:key="item.id"
|
||||||
|
:class="{ 'bg-primary/20': currentSelected === item }"
|
||||||
|
class="flex gap-2 hover:bg-primary/20 px-4 items-center"
|
||||||
|
@click="currentSelected = item"
|
||||||
|
>
|
||||||
|
<button class="link flex items-center no-underline w-full py-2">
|
||||||
|
<input
|
||||||
|
v-model="item.key"
|
||||||
|
:readonly="currentSelected !== item || read_only"
|
||||||
|
class="flex-1 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
v-if="!read_only"
|
||||||
|
:class="[currentSelected === item ? 'visible' : 'invisible']"
|
||||||
|
class="inline-flex btn-square btn-error btn-outline"
|
||||||
|
@click="deleteItem(item.id)"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:trash-outline"
|
||||||
|
class="size-5"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</li>
|
||||||
|
</UiList>
|
||||||
|
|
||||||
|
<UiTextarea
|
||||||
|
v-if="items.length || itemsToAdd.length"
|
||||||
|
:read_only="read_only || !currentSelected"
|
||||||
|
class="flex-1 min-w-52 border-base-content/25"
|
||||||
|
rows="6"
|
||||||
|
v-model="currentValue"
|
||||||
|
with-copy-button
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="!read_only"
|
||||||
|
class="flex py-4 gap-2 justify-center items-end flex-wrap"
|
||||||
|
>
|
||||||
|
<UiButton
|
||||||
|
@click="addItem"
|
||||||
|
class="btn-primary btn-outline flex-1-1 min-w-40"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:plus" />
|
||||||
|
<p class="hidden sm:inline-flex">{{ t('add') }}</p>
|
||||||
|
</UiButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectHaexPasswordsItemKeyValues } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
const { itemId } = defineProps<{ read_only?: boolean; itemId: string }>()
|
||||||
|
|
||||||
|
const items = defineModel<SelectHaexPasswordsItemKeyValues[]>({ default: [] })
|
||||||
|
|
||||||
|
const itemsToDelete = defineModel<SelectHaexPasswordsItemKeyValues[]>(
|
||||||
|
'itemsToDelete',
|
||||||
|
{ default: [] },
|
||||||
|
)
|
||||||
|
const itemsToAdd = defineModel<SelectHaexPasswordsItemKeyValues[]>(
|
||||||
|
'itemsToAdd',
|
||||||
|
{ default: [] },
|
||||||
|
)
|
||||||
|
|
||||||
|
defineEmits<{ add: [void]; remove: [string] }>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const currentSelected = ref<SelectHaexPasswordsItemKeyValues | undefined>(
|
||||||
|
items.value?.at(0),
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => itemId,
|
||||||
|
() => (currentSelected.value = items.value?.at(0)),
|
||||||
|
)
|
||||||
|
//const currentValue = computed(() => currentSelected.value?.value || '')
|
||||||
|
const currentValue = computed({
|
||||||
|
get: () => currentSelected.value?.value || '',
|
||||||
|
set(newValue: string) {
|
||||||
|
if (currentSelected.value) currentSelected.value.value = newValue
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const addItem = () => {
|
||||||
|
itemsToAdd.value?.push({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
itemId,
|
||||||
|
key: '',
|
||||||
|
value: '',
|
||||||
|
updateAt: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteItem = (id: string) => {
|
||||||
|
const item = items.value.find((item) => item.id === id)
|
||||||
|
if (item) {
|
||||||
|
itemsToDelete.value?.push(item)
|
||||||
|
items.value = items.value.filter((item) => item.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsToAdd.value = itemsToAdd.value?.filter((item) => item.id !== id) ?? []
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
add: Hinzufügen
|
||||||
|
key: Schlüssel
|
||||||
|
value: Wert
|
||||||
|
|
||||||
|
en:
|
||||||
|
add: Add
|
||||||
|
key: Key
|
||||||
|
value: Value
|
||||||
|
</i18n>
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button
|
|
||||||
aria-disabled
|
|
||||||
class="flex gap-4 w-full"
|
|
||||||
@click="$emit('click', group)"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
:name="groupIcon"
|
|
||||||
size="24"
|
|
||||||
class="shrink-0"
|
|
||||||
/>
|
|
||||||
<p class="w-full flex-1 text-start truncate">
|
|
||||||
{{ group.name }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Icon
|
|
||||||
name="mdi:chevron-right"
|
|
||||||
size="24"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
|
||||||
|
|
||||||
defineEmits<{ click: [group: SelectHaexPasswordsGroups] }>()
|
|
||||||
|
|
||||||
const { group } = defineProps<{
|
|
||||||
group: SelectHaexPasswordsGroups
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const groupIcon = computed(() => group.icon ?? 'mdi:folder-outline')
|
|
||||||
</script>
|
|
||||||
@ -1,78 +1,124 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul
|
<div v-if="menuItems?.length">
|
||||||
class="flex flex-col w-full h-full gap-y-2 *:first:rounded-t-md *:last:rounded-b-md"
|
<ul
|
||||||
ref="listRef"
|
class="flex flex-col w-full h-full gap-y-2 *:first:rounded-t-md *:last:rounded-b-md"
|
||||||
|
ref="listRef"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in menuItems"
|
||||||
|
:key="item.id"
|
||||||
|
class="bg-base-100 rounded-lg hover:bg-base-content/20 origin-to intersect:motion-preset-slide-down intersect:motion-ease-spring-bouncier intersect:motion-delay ease-in-out shadow"
|
||||||
|
:class="{
|
||||||
|
'bg-base-content/30 outline outline-accent hover:bg-base-content/20':
|
||||||
|
selectedItems.has(item) ||
|
||||||
|
(currentSelectedItem?.id === item.id &&
|
||||||
|
longPressedHook &&
|
||||||
|
!selectedItems.has(item)),
|
||||||
|
'opacity-60 shadow-accent': selectedGroupItems?.some(
|
||||||
|
(_item) => _item.id === item.id,
|
||||||
|
),
|
||||||
|
}"
|
||||||
|
:style="{ '--motion-delay': `${50 * index}ms` }"
|
||||||
|
v-on-long-press="[
|
||||||
|
onLongPressCallbackHook,
|
||||||
|
{
|
||||||
|
delay: 1000,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
@mousedown="
|
||||||
|
longPressedHook
|
||||||
|
? (currentSelectedItem = null)
|
||||||
|
: (currentSelectedItem = item)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<HaexPassMobileMenuItem
|
||||||
|
v-bind="item"
|
||||||
|
@click="onClickItemAsync(item)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex justify-center items-center px-20 h-full"
|
||||||
>
|
>
|
||||||
<li
|
<UiIconNoData class="text-primary size-24 shrink-0" />
|
||||||
v-for="(group, index) in groupItems.groups"
|
<!-- <p>{{ t('empty') }}</p> -->
|
||||||
class="bg-base-100 rounded-lg hover:bg-base-content/20 origin-to intersect:motion-preset-slide-down intersect:motion-ease-spring-bouncier intersect:motion-delay ease-in-out shadow"
|
</div>
|
||||||
:class="{
|
|
||||||
'bg-base-content/20 outline outline-accent hover:bg-base-content/20':
|
|
||||||
selectedItems.has(group.id),
|
|
||||||
}"
|
|
||||||
:style="{ '--motion-delay': `${50 * index}ms` }"
|
|
||||||
:key="group.id"
|
|
||||||
v-on-long-press="[
|
|
||||||
onLongPressCallbackHook,
|
|
||||||
{
|
|
||||||
delay: 1000,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<HaexPassMobileMenuGroup
|
|
||||||
:group
|
|
||||||
@click="onClickGroupAsync"
|
|
||||||
class="px-4 py-2"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-for="item in groupItems.items"
|
|
||||||
:key="item.id"
|
|
||||||
>
|
|
||||||
<HaexPassMobileMenuItem :item />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
|
||||||
SelectHaexPasswordsGroups,
|
|
||||||
SelectHaexPasswordsItems,
|
|
||||||
} from '~~/src-tauri/database/schemas/vault'
|
|
||||||
|
|
||||||
import { vOnLongPress } from '@vueuse/components'
|
import { vOnLongPress } from '@vueuse/components'
|
||||||
|
import type { IPasswordMenuItem } from './types'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
groupItems: {
|
menuItems: IPasswordMenuItem[]
|
||||||
items: SelectHaexPasswordsItems[]
|
|
||||||
groups: SelectHaexPasswordsGroups[]
|
|
||||||
}
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const selectedItems = ref<Set<string>>(new Set())
|
defineEmits(['add'])
|
||||||
const longPressedHook = shallowRef(false)
|
const selectedItems = defineModel<Set<IPasswordMenuItem>>('selectedItems', {
|
||||||
|
default: new Set(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentSelectedItem = ref<IPasswordMenuItem | null>()
|
||||||
|
|
||||||
|
const longPressedHook = ref(false)
|
||||||
|
|
||||||
const onLongPressCallbackHook = (_: PointerEvent) => {
|
const onLongPressCallbackHook = (_: PointerEvent) => {
|
||||||
longPressedHook.value = true
|
longPressedHook.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(longPressedHook, () => {
|
||||||
|
if (!longPressedHook.value) selectedItems.value.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(selectedItems, () => {
|
||||||
|
if (!selectedItems.value.size) longPressedHook.value = false
|
||||||
|
})
|
||||||
|
|
||||||
const localePath = useLocalePath()
|
const localePath = useLocalePath()
|
||||||
const onClickGroupAsync = async (group: SelectHaexPasswordsGroups) => {
|
const { ctrl } = useMagicKeys()
|
||||||
if (longPressedHook.value) {
|
|
||||||
if (selectedItems.value.has(group.id)) {
|
const onClickItemAsync = async (item: IPasswordMenuItem) => {
|
||||||
selectedItems.value.delete(group.id)
|
currentSelectedItem.value = null
|
||||||
|
|
||||||
|
if (longPressedHook.value || selectedItems.value.size || ctrl.value) {
|
||||||
|
if (selectedItems.value?.has(item)) {
|
||||||
|
selectedItems.value.delete(item)
|
||||||
} else {
|
} else {
|
||||||
selectedItems.value.add(group.id)
|
selectedItems.value?.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedItems.value.size) longPressedHook.value = false
|
if (!selectedItems.value.size) longPressedHook.value = false
|
||||||
} else {
|
} else {
|
||||||
await navigateTo(localePath({ name: 'passwordGroupEdit' }))
|
if (item.type === 'group')
|
||||||
|
await navigateTo(
|
||||||
|
localePath({
|
||||||
|
name: 'passwordGroupItems',
|
||||||
|
params: {
|
||||||
|
...useRouter().currentRoute.value.params,
|
||||||
|
groupId: item.id,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
await navigateTo(
|
||||||
|
localePath({
|
||||||
|
name: 'passwordItemEdit',
|
||||||
|
params: { ...useRouter().currentRoute.value.params, itemId: item.id },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const listRef = useTemplateRef('listRef')
|
const listRef = useTemplateRef('listRef')
|
||||||
onClickOutside(listRef, () => {
|
onClickOutside(listRef, async () => {
|
||||||
selectedItems.value.clear()
|
// needed cause otherwise the unselect is to fast for other processing like "edit selected group"
|
||||||
longPressedHook.value = false
|
setTimeout(() => {
|
||||||
|
longPressedHook.value = false
|
||||||
|
}, 50)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { selectedGroupItems } = storeToRefs(usePasswordGroupStore())
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,13 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<NuxtLinkLocale>
|
<button
|
||||||
{{ item.title }}
|
aria-disabled
|
||||||
</NuxtLinkLocale>
|
class="flex gap-4 w-full px-4 py-2"
|
||||||
|
@click="$emit('click', menuItem)"
|
||||||
|
:style="{ color: menuItem.color ?? '' }"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="menuIcon"
|
||||||
|
size="24"
|
||||||
|
class="shrink-0"
|
||||||
|
/>
|
||||||
|
<p class="w-full flex-1 text-start truncate font-bold">
|
||||||
|
{{ menuItem?.name }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
v-if="menuItem.type === 'group'"
|
||||||
|
name="mdi:chevron-right"
|
||||||
|
size="24"
|
||||||
|
class="text-base-content"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
import type { IPasswordMenuItem } from './types'
|
||||||
|
|
||||||
defineProps<{
|
defineEmits<{ click: [group?: IPasswordMenuItem] }>()
|
||||||
item: SelectHaexPasswordsItems
|
|
||||||
}>()
|
const menuItem = defineProps<IPasswordMenuItem>()
|
||||||
|
|
||||||
|
const menuIcon = computed(() =>
|
||||||
|
menuItem?.icon
|
||||||
|
? menuItem.icon
|
||||||
|
: menuItem.type === 'group'
|
||||||
|
? 'mdi:folder-outline'
|
||||||
|
: 'mdi:key-outline',
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
export interface IPassMenuItem {
|
export interface IPasswordMenuItem {
|
||||||
name: string
|
color?: string | null
|
||||||
type: 'group' | 'item'
|
icon: string | null
|
||||||
id: string
|
id: string
|
||||||
|
name: string | null
|
||||||
|
type: 'group' | 'item'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="z-10">
|
<div class="z-10 pointer-events-auto">
|
||||||
<div
|
<div
|
||||||
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
|
class="dropdown relative inline-flex [--placement:top] [--strategy:absolute]"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
:id
|
:id
|
||||||
class="dropdown-toggle btn btn-primary btn-lg btn-square dropdown-open:rotate-45 transition-transform"
|
class="dropdown-toggle btn btn-primary btn-xl btn-square dropdown-open:rotate-45 transition-transform"
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
:name="icon"
|
:name="icon"
|
||||||
size="46"
|
class="size-11 shrink-0"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
v-for="link in menu"
|
v-for="link in menu"
|
||||||
class="dropdown-item hover:bg-transparent px-0"
|
class="dropdown-item hover:bg-transparent px-0 py-1"
|
||||||
>
|
>
|
||||||
<NuxtLinkLocale
|
<NuxtLinkLocale
|
||||||
v-if="link.to"
|
v-if="link.to"
|
||||||
|
|||||||
@ -1,14 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="btn join-item" :type>
|
<button
|
||||||
<slot />
|
class="btn join-item pointer-events-auto"
|
||||||
|
:type
|
||||||
|
>
|
||||||
|
<UiTooltip
|
||||||
|
:tooltip
|
||||||
|
v-if="tooltip"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</UiTooltip>
|
||||||
|
|
||||||
|
<slot v-else />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
const { type = 'button' } = defineProps<{
|
||||||
type: {
|
type?: 'reset' | 'submit' | 'button'
|
||||||
type: String as PropType<"reset" | "submit" | "button">,
|
tooltip?: string
|
||||||
default: "button",
|
}>()
|
||||||
},
|
</script>
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<slot name="image" />
|
<slot name="image" />
|
||||||
|
|
||||||
<div class="card-header">
|
<div
|
||||||
|
class="card-header"
|
||||||
|
v-if="$slots.title || title"
|
||||||
|
>
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<div
|
<div
|
||||||
v-if="$slots.title || title"
|
v-if="$slots.title || title"
|
||||||
@ -28,7 +31,10 @@
|
|||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body px-2 sm:px-6">
|
<div
|
||||||
|
class="card-body"
|
||||||
|
:class="bodyClass"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
<div
|
<div
|
||||||
v-if="$slots.action"
|
v-if="$slots.action"
|
||||||
@ -37,13 +43,25 @@
|
|||||||
<slot name="action" />
|
<slot name="action" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="$slots.footer"
|
||||||
|
class="card-footer"
|
||||||
|
>
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const emit = defineEmits(['close', 'submit'])
|
const emit = defineEmits(['close', 'submit'])
|
||||||
|
|
||||||
defineProps<{ title?: string; subtitle?: string; icon?: string }>()
|
defineProps<{
|
||||||
|
title?: string
|
||||||
|
subtitle?: string
|
||||||
|
icon?: string
|
||||||
|
bodyClass?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
const { escape, enter } = useMagicKeys()
|
const { escape, enter } = useMagicKeys()
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiDialog
|
<UiDialog
|
||||||
v-model:open="open"
|
:title
|
||||||
@close="onAbort"
|
@close="onAbort"
|
||||||
|
v-model:open="open"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<slot name="trigger" />
|
<slot name="trigger" />
|
||||||
@ -33,19 +34,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps<{ confirmLabel?: string; abortLabel?: string }>()
|
defineProps<{ confirmLabel?: string; abortLabel?: string; title?: string }>()
|
||||||
|
|
||||||
const open = defineModel<boolean>('open', { default: false })
|
const open = defineModel<boolean>('open', { default: false })
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const emit = defineEmits(['confirm', 'abort'])
|
const emit = defineEmits(['confirm', 'abort'])
|
||||||
|
|
||||||
const onAbort = () => {
|
const onAbort = () => {
|
||||||
emit('abort')
|
emit('abort')
|
||||||
open.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
emit('confirm')
|
emit('confirm')
|
||||||
open.value = false
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
aria-haspopup="dialog"
|
aria-haspopup="dialog"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
:aria-label="label"
|
:aria-label="label"
|
||||||
class="--prevent-on-load-init"
|
|
||||||
@click="$emit('open')"
|
@click="$emit('open')"
|
||||||
>
|
>
|
||||||
<slot name="trigger">
|
<slot name="trigger">
|
||||||
@ -14,55 +13,58 @@
|
|||||||
</slot>
|
</slot>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<Teleport to="body">
|
||||||
:id
|
|
||||||
ref="modalRef"
|
|
||||||
class="overlay modal overlay-open:opacity-100 hidden overlay-open:duration-300 sm:modal-middle p-0 xs:p-2"
|
|
||||||
role="dialog"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="overlay-animation-target overlay-open:duration-300 transition-all ease-out modal-dialog overlay-open:opacity-100 pointer-events-auto overflow-y-auto"
|
:id
|
||||||
|
ref="modalRef"
|
||||||
|
class="overlay modal overlay-open:opacity-100 overlay-open:duration-300 hidden modal-middle p-0 xs:p-2 --prevent-on-load-init pointer-events-auto max-w-none"
|
||||||
|
role="dialog"
|
||||||
|
tabindex="-1"
|
||||||
>
|
>
|
||||||
<div class="modal-content justify-between h-full max-h-none">
|
<div
|
||||||
<div class="modal-header">
|
class="overlay-animation-target overlay-open:duration-300 overlay-open:opacity-100 transition-all ease-out modal-dialog"
|
||||||
<div
|
>
|
||||||
v-if="title || $slots.title"
|
<div class="modal-content justify-between">
|
||||||
class="modal-title py-4 break-all"
|
<div class="modal-header">
|
||||||
>
|
<div
|
||||||
<slot name="title">
|
v-if="title || $slots.title"
|
||||||
{{ title }}
|
class="modal-title py-4 break-all"
|
||||||
</slot>
|
>
|
||||||
|
<slot name="title">
|
||||||
|
{{ title }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
||||||
|
:aria-label="t('close')"
|
||||||
|
tabindex="1"
|
||||||
|
@click="open = false"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:close"
|
||||||
|
size="18"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<div class="modal-body text-sm sm:text-base grow">
|
||||||
type="button"
|
<slot />
|
||||||
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
</div>
|
||||||
:aria-label="t('close')"
|
|
||||||
tabindex="1"
|
|
||||||
@click="open = false"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
name="mdi:close"
|
|
||||||
size="18"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-body text-sm sm:text-base">
|
<div class="modal-footer flex-col sm:flex-row">
|
||||||
<slot />
|
<slot name="buttons" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer flex-col sm:flex-row">
|
|
||||||
<slot name="buttons" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { HSOverlay } from 'flyonui/flyonui'
|
import type { HSOverlay } from 'flyonui/flyonui'
|
||||||
|
const { currentTheme } = storeToRefs(useUiStore())
|
||||||
|
|
||||||
defineProps<{ title?: string; label?: string }>()
|
defineProps<{ title?: string; label?: string }>()
|
||||||
|
|
||||||
@ -81,10 +83,12 @@ defineExpose({ modalRef })
|
|||||||
const modal = ref<HSOverlay>()
|
const modal = ref<HSOverlay>()
|
||||||
|
|
||||||
watch(open, async () => {
|
watch(open, async () => {
|
||||||
|
if (!modal.value) return
|
||||||
|
|
||||||
if (open.value) {
|
if (open.value) {
|
||||||
await modal.value?.open()
|
await modal.value.open()
|
||||||
} else {
|
} else {
|
||||||
await modal.value?.close(true)
|
await modal.value.close(true)
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -92,10 +96,9 @@ watch(open, async () => {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (!modalRef.value) return
|
if (!modalRef.value) return
|
||||||
|
|
||||||
modal.value = new window.HSOverlay(modalRef.value, {
|
modal.value = new window.HSOverlay(modalRef.value)
|
||||||
isClosePrev: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
modal.value.isLayoutAffect = true
|
||||||
modal.value.on('close', () => {
|
modal.value.on('close', () => {
|
||||||
open.value = false
|
open.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
55
src/components/ui/dialog/passwordGenerator.vue
Normal file
55
src/components/ui/dialog/passwordGenerator.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<UiDialogConfirm
|
||||||
|
:confirm-label="t('apply')"
|
||||||
|
:title="t('title')"
|
||||||
|
@abort="open = false"
|
||||||
|
@click="open = true"
|
||||||
|
class="btn btn-square btn-accent btn-outline"
|
||||||
|
v-model:open="open"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<Icon name="mdi:dice" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<form class="flex flex-col gap-4">
|
||||||
|
<UiInputPassword
|
||||||
|
v-model="newPassword"
|
||||||
|
prepend-icon="mdi:key-outline"
|
||||||
|
with-copy-button
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<UiButton class="btn-square btn-accent btn-outline">
|
||||||
|
<Icon name="mdi:refresh" />
|
||||||
|
</UiButton>
|
||||||
|
</template>
|
||||||
|
</UiInputPassword>
|
||||||
|
</form>
|
||||||
|
</UiDialogConfirm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const open = defineModel<boolean>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { password } = defineProps<{
|
||||||
|
autofocus?: boolean
|
||||||
|
checkInput?: boolean
|
||||||
|
label?: string
|
||||||
|
placeholder?: string
|
||||||
|
withCopyButton?: boolean
|
||||||
|
password: string | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const newPassword = computed(() => password)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
title: Passwortgenerator
|
||||||
|
apply: Übernehmen
|
||||||
|
|
||||||
|
en:
|
||||||
|
title: Passwordgenerator
|
||||||
|
apply: Apply
|
||||||
|
</i18n>
|
||||||
@ -1,15 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="dropdown relative inline-flex"
|
|
||||||
:class="offset"
|
:class="offset"
|
||||||
|
class="dropdown relative inline-flex"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
:id
|
|
||||||
class="dropdown-toggle"
|
|
||||||
v-bind="$attrs"
|
|
||||||
aria-haspopup="menu"
|
|
||||||
aria-expanded="false"
|
|
||||||
:aria-label="label"
|
:aria-label="label"
|
||||||
|
:id
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="menu"
|
||||||
|
class="dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
<slot name="activator">
|
<slot name="activator">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
@ -20,21 +21,24 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28"
|
|
||||||
role="menu"
|
|
||||||
aria-orientation="vertical"
|
|
||||||
:aria-labelledby="id"
|
:aria-labelledby="id"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
class="dropdown-menu dropdown-open:opacity-100 hidden min-w-28 z-20 shadow shadow-primary"
|
||||||
|
role="menu"
|
||||||
>
|
>
|
||||||
<slot name="items">
|
<slot
|
||||||
|
name="items"
|
||||||
|
:items
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
:is="itemIs"
|
:is="itemIs"
|
||||||
v-for="item in items"
|
|
||||||
class="dropdown-item"
|
|
||||||
@click="$emit('select', item)"
|
@click="$emit('select', item)"
|
||||||
|
class="dropdown-item"
|
||||||
|
v-for="item in items"
|
||||||
>
|
>
|
||||||
<slot
|
<slot
|
||||||
name="item"
|
|
||||||
:item
|
:item
|
||||||
|
name="item"
|
||||||
>
|
>
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</slot>
|
</slot>
|
||||||
@ -45,6 +49,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T">
|
<script setup lang="ts" generic="T">
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
|
||||||
const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
|
const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
|
||||||
label?: string
|
label?: string
|
||||||
items?: T[]
|
items?: T[]
|
||||||
@ -53,10 +61,6 @@ const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
|
|||||||
offset?: string
|
offset?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
inheritAttrs: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
defineEmits<{ select: [T] }>()
|
defineEmits<{ select: [T] }>()
|
||||||
|
|
||||||
const id = useId()
|
const id = useId()
|
||||||
|
|||||||
132
src/components/ui/icon/addFiles.vue
Normal file
132
src/components/ui/icon/addFiles.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 782.04441 701.88002"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
role="img"
|
||||||
|
artist="Katerina Limpitsouni"
|
||||||
|
source="https://undraw.co/"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M609.48783,100.59015l-25.44631,6.56209L270.53735,187.9987,245.091,194.56079A48.17927,48.17927,0,0,0,210.508,253.17865L320.849,681.05606a48.17924,48.17924,0,0,0,58.61776,34.58317l.06572-.01695,364.26536-93.93675.06572-.01695a48.17923,48.17923,0,0,0,34.58309-58.6178l-110.341-427.87741A48.17928,48.17928,0,0,0,609.48783,100.59015Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M612.94784,114.00532l-30.13945,7.77236L278.68955,200.20385l-30.139,7.77223a34.30949,34.30949,0,0,0-24.6275,41.74308l110.341,427.87741a34.30946,34.30946,0,0,0,41.7431,24.62736l.06572-.01695,364.26536-93.93674.06619-.01707a34.30935,34.30935,0,0,0,24.627-41.7429l-110.341-427.87741A34.30938,34.30938,0,0,0,612.94784,114.00532Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M590.19,252.56327,405.917,300.08359a8.01411,8.01411,0,0,1-4.00241-15.52046l184.273-47.52033A8.01412,8.01412,0,0,1,590.19,252.56327Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M628.955,270.49906,412.671,326.27437a8.01411,8.01411,0,1,1-4.00241-15.52046l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M620.45825,369.93676l-184.273,47.52032a8.01411,8.01411,0,1,1-4.00242-15.52046l184.273-47.52032a8.01411,8.01411,0,1,1,4.00241,15.52046Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M659.22329,387.87255l-216.284,55.77531a8.01411,8.01411,0,1,1-4.00242-15.52046l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M650.72653,487.31025l-184.273,47.52033a8.01412,8.01412,0,0,1-4.00242-15.52047l184.273-47.52032a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M689.49156,505.246l-216.284,55.77532a8.01412,8.01412,0,1,1-4.00241-15.52047l216.284-55.77531a8.01411,8.01411,0,0,1,4.00242,15.52046Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M374.45884,348.80871l-65.21246,16.817a3.847,3.847,0,0,1-4.68062-2.76146L289.5963,304.81607a3.847,3.847,0,0,1,2.76145-4.68061l65.21247-16.817a3.847,3.847,0,0,1,4.68061,2.76145l14.96947,58.04817A3.847,3.847,0,0,1,374.45884,348.80871Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M404.72712,466.1822l-65.21247,16.817a3.847,3.847,0,0,1-4.68062-2.76146l-14.96946-58.04816A3.847,3.847,0,0,1,322.626,417.509l65.21246-16.817a3.847,3.847,0,0,1,4.68062,2.76145l14.96946,58.04817A3.847,3.847,0,0,1,404.72712,466.1822Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M434.99539,583.55569l-65.21246,16.817a3.847,3.847,0,0,1-4.68062-2.76145l-14.96946-58.04817a3.847,3.847,0,0,1,2.76145-4.68062l65.21247-16.817a3.847,3.847,0,0,1,4.68061,2.76146l14.96947,58.04816A3.847,3.847,0,0,1,434.99539,583.55569Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M863.63647,209.0517H487.31811a48.17928,48.17928,0,0,0-48.125,48.12512V699.05261a48.17924,48.17924,0,0,0,48.125,48.12507H863.63647a48.17924,48.17924,0,0,0,48.125-48.12507V257.17682A48.17928,48.17928,0,0,0,863.63647,209.0517Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M863.637,222.90589H487.31811a34.30948,34.30948,0,0,0-34.271,34.27093V699.05261a34.30947,34.30947,0,0,0,34.271,34.27088H863.637a34.30936,34.30936,0,0,0,34.27051-34.27088V257.17682A34.30937,34.30937,0,0,0,863.637,222.90589Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="694.19401"
|
||||||
|
cy="614.02963"
|
||||||
|
r="87.85039"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M945.18722,701.63087H914.63056V671.07421a11.45875,11.45875,0,0,0-22.9175,0v30.55666H861.1564a11.45875,11.45875,0,0,0,0,22.9175h30.55666V755.105a11.45875,11.45875,0,1,0,22.9175,0V724.54837h30.55666a11.45875,11.45875,0,0,0,0-22.9175Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M807.00068,465.71551H616.699a8.01412,8.01412,0,1,1,0-16.02823H807.00068a8.01412,8.01412,0,0,1,0,16.02823Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M840.05889,492.76314H616.699a8.01412,8.01412,0,1,1,0-16.02823H840.05889a8.01411,8.01411,0,1,1,0,16.02823Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M807.00068,586.929H616.699a8.01412,8.01412,0,1,1,0-16.02823H807.00068a8.01411,8.01411,0,0,1,0,16.02823Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M840.05889,613.97661H616.699a8.01412,8.01412,0,1,1,0-16.02823H840.05889a8.01412,8.01412,0,1,1,0,16.02823Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M574.07028,505.04162H506.72434a3.847,3.847,0,0,1-3.84278-3.84278V441.25158a3.847,3.847,0,0,1,3.84278-3.84278h67.34594a3.847,3.847,0,0,1,3.84278,3.84278v59.94726A3.847,3.847,0,0,1,574.07028,505.04162Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M574.07028,626.25509H506.72434a3.847,3.847,0,0,1-3.84278-3.84278V562.46505a3.847,3.847,0,0,1,3.84278-3.84278h67.34594a3.847,3.847,0,0,1,3.84278,3.84278v59.94726A3.847,3.847,0,0,1,574.07028,626.25509Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M807.21185,330.781H666.91017a8.01411,8.01411,0,0,1,0-16.02823H807.21185a8.01411,8.01411,0,0,1,0,16.02823Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#ccc"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M840.27007,357.82862H666.91017a8.01411,8.01411,0,1,1,0-16.02822h173.3599a8.01411,8.01411,0,0,1,0,16.02822Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="#ccc"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M635.85911,390.6071H506.51316a3.847,3.847,0,0,1-3.84277-3.84277V285.81706a3.847,3.847,0,0,1,3.84277-3.84277H635.85911a3.847,3.847,0,0,1,3.84277,3.84277V386.76433A3.847,3.847,0,0,1,635.85911,390.6071Z"
|
||||||
|
transform="translate(-208.9778 -99.05999)"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
65
src/components/ui/icon/noData.vue
Normal file
65
src/components/ui/icon/noData.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 647.63626 632.17383"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
role="img"
|
||||||
|
artist="Katerina Limpitsouni"
|
||||||
|
source="https://undraw.co/"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M687.3279,276.08691H512.81813a15.01828,15.01828,0,0,0-15,15v387.85l-2,.61005-42.81006,13.11a8.00676,8.00676,0,0,1-9.98974-5.31L315.678,271.39691a8.00313,8.00313,0,0,1,5.31006-9.99l65.97022-20.2,191.25-58.54,65.96972-20.2a7.98927,7.98927,0,0,1,9.99024,5.3l32.5498,106.32Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="#f2f2f2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M725.408,274.08691l-39.23-128.14a16.99368,16.99368,0,0,0-21.23-11.28l-92.75,28.39L380.95827,221.60693l-92.75,28.4a17.0152,17.0152,0,0,0-11.28028,21.23l134.08008,437.93a17.02661,17.02661,0,0,0,16.26026,12.03,16.78926,16.78926,0,0,0,4.96972-.75l63.58008-19.46,2-.62v-2.09l-2,.61-64.16992,19.65a15.01489,15.01489,0,0,1-18.73-9.95l-134.06983-437.94a14.97935,14.97935,0,0,1,9.94971-18.73l92.75-28.4,191.24024-58.54,92.75-28.4a15.15551,15.15551,0,0,1,4.40966-.66,15.01461,15.01461,0,0,1,14.32032,10.61l39.0498,127.56.62012,2h2.08008Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="#3f3d56"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M398.86279,261.73389a9.0157,9.0157,0,0,1-8.61133-6.3667l-12.88037-42.07178a8.99884,8.99884,0,0,1,5.9712-11.24023l175.939-53.86377a9.00867,9.00867,0,0,1,11.24072,5.9707l12.88037,42.07227a9.01029,9.01029,0,0,1-5.9707,11.24072L401.49219,261.33887A8.976,8.976,0,0,1,398.86279,261.73389Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="190.15351"
|
||||||
|
cy="24.95465"
|
||||||
|
r="20"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="190.15351"
|
||||||
|
cy="24.95465"
|
||||||
|
r="12.66462"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M878.81836,716.08691h-338a8.50981,8.50981,0,0,1-8.5-8.5v-405a8.50951,8.50951,0,0,1,8.5-8.5h338a8.50982,8.50982,0,0,1,8.5,8.5v405A8.51013,8.51013,0,0,1,878.81836,716.08691Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="#e6e6e6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M723.31813,274.08691h-210.5a17.02411,17.02411,0,0,0-17,17v407.8l2-.61v-407.19a15.01828,15.01828,0,0,1,15-15H723.93825Zm183.5,0h-394a17.02411,17.02411,0,0,0-17,17v458a17.0241,17.0241,0,0,0,17,17h394a17.0241,17.0241,0,0,0,17-17v-458A17.02411,17.02411,0,0,0,906.81813,274.08691Zm15,475a15.01828,15.01828,0,0,1-15,15h-394a15.01828,15.01828,0,0,1-15-15v-458a15.01828,15.01828,0,0,1,15-15h394a15.01828,15.01828,0,0,1,15,15Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="#3f3d56"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M801.81836,318.08691h-184a9.01015,9.01015,0,0,1-9-9v-44a9.01016,9.01016,0,0,1,9-9h184a9.01016,9.01016,0,0,1,9,9v44A9.01015,9.01015,0,0,1,801.81836,318.08691Z"
|
||||||
|
transform="translate(-276.18187 -133.91309)"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="433.63626"
|
||||||
|
cy="105.17383"
|
||||||
|
r="20"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="433.63626"
|
||||||
|
cy="105.17383"
|
||||||
|
r="12.18187"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
@ -29,8 +29,9 @@
|
|||||||
<label
|
<label
|
||||||
class="input-floating-label"
|
class="input-floating-label"
|
||||||
:for="id"
|
:for="id"
|
||||||
>{{ label }}</label
|
|
||||||
>
|
>
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
@ -47,7 +48,8 @@
|
|||||||
|
|
||||||
<UiButton
|
<UiButton
|
||||||
v-if="withCopyButton"
|
v-if="withCopyButton"
|
||||||
class="btn-outline btn-accent btn-square join-item h-auto"
|
:tooltip="t('copy')"
|
||||||
|
class="btn-outline btn-accent btn-square"
|
||||||
@click="copy(`${input}`)"
|
@click="copy(`${input}`)"
|
||||||
>
|
>
|
||||||
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||||
@ -167,4 +169,14 @@ const checkInput = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { copy, copied } = useClipboard()
|
const { copy, copied } = useClipboard()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
copy: Kopieren
|
||||||
|
|
||||||
|
en:
|
||||||
|
copy: Copy
|
||||||
|
</i18n>
|
||||||
|
|||||||
@ -1,9 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiInput
|
<UiInput
|
||||||
v-model="value" :check-input :label="label || t('password')" :placeholder="placeholder || t('password')" :rules
|
v-model="value"
|
||||||
:type="type" :autofocus>
|
:autofocus
|
||||||
|
:check-input
|
||||||
|
:label="label || t('password')"
|
||||||
|
:placeholder="placeholder || t('password')"
|
||||||
|
:rules
|
||||||
|
:type="type"
|
||||||
|
:with-copy-button
|
||||||
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<UiButton class="btn-outline btn-accent btn-square h-auto" @click="tooglePasswordType">
|
<slot name="append" />
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
class="btn-outline btn-accent btn-square join-item"
|
||||||
|
@click="tooglePasswordType"
|
||||||
|
>
|
||||||
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
|
<Icon :name="type === 'password' ? 'mdi:eye-off' : 'mdi:eye'" />
|
||||||
</UiButton>
|
</UiButton>
|
||||||
</template>
|
</template>
|
||||||
@ -11,33 +23,35 @@ v-model="value" :check-input :label="label || t('password')" :placeholder="place
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ZodSchema } from "zod";
|
import type { ZodSchema } from 'zod'
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n()
|
||||||
|
|
||||||
const value = defineModel<string | number | null | undefined>();
|
const value = defineModel<string | number | null | undefined>()
|
||||||
|
|
||||||
defineProps({
|
defineProps<{
|
||||||
label: String,
|
autofocus?: boolean
|
||||||
placeholder: String,
|
checkInput?: boolean
|
||||||
checkInput: Boolean,
|
label?: string
|
||||||
rules: Object as PropType<ZodSchema>,
|
placeholder?: string
|
||||||
autofocus: Boolean,
|
rules?: ZodSchema
|
||||||
});
|
withCopyButton?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
const type = ref<"password" | "text">("password");
|
const type = ref<'password' | 'text'>('password')
|
||||||
|
|
||||||
const tooglePasswordType = () => {
|
const tooglePasswordType = () => {
|
||||||
type.value = type.value === "password" ? "text" : "password";
|
type.value = type.value === 'password' ? 'text' : 'password'
|
||||||
};
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="json">{
|
<i18n lang="json">
|
||||||
|
{
|
||||||
"de": {
|
"de": {
|
||||||
"password": "Passwort"
|
"password": "Passwort"
|
||||||
},
|
},
|
||||||
"en": {
|
"en": {
|
||||||
"password": "Password"
|
"password": "Password"
|
||||||
}
|
}
|
||||||
}</i18n>
|
}
|
||||||
|
</i18n>
|
||||||
|
|||||||
52
src/components/ui/input/url.vue
Normal file
52
src/components/ui/input/url.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<UiInput
|
||||||
|
:autofocus
|
||||||
|
:check-input="checkInput"
|
||||||
|
:label="label || t('url')"
|
||||||
|
:placeholder="placeholder || t('url')"
|
||||||
|
:read_only
|
||||||
|
:rules
|
||||||
|
:with-copy-button
|
||||||
|
v-model.trim="value"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<UiButton
|
||||||
|
:disabled="!value?.length"
|
||||||
|
@click="openUrl(`${value}`)"
|
||||||
|
class="btn-outline btn-accent btn-square"
|
||||||
|
>
|
||||||
|
<Icon name="streamline:web" />
|
||||||
|
</UiButton>
|
||||||
|
</template>
|
||||||
|
</UiInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { ZodSchema } from 'zod'
|
||||||
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const value = defineModel<string | null | undefined>()
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
label: String,
|
||||||
|
placeholder: String,
|
||||||
|
checkInput: Boolean,
|
||||||
|
rules: Object as PropType<ZodSchema>,
|
||||||
|
autofocus: Boolean,
|
||||||
|
withCopyButton: Boolean,
|
||||||
|
read_only: Boolean,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="json">
|
||||||
|
{
|
||||||
|
"de": {
|
||||||
|
"url": "Url"
|
||||||
|
},
|
||||||
|
"en": {
|
||||||
|
"url": "Url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
||||||
7
src/components/ui/list/index.vue
Normal file
7
src/components/ui/list/index.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex flex-col h-fit border border-base-content/25 divide-base-content/25 divide-y rounded-md first:rounded-t-md last:rounded-b-md"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -1,29 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4 relative">
|
||||||
<label
|
<UiButton
|
||||||
:for="id"
|
:style="{ 'background-color': model }"
|
||||||
class="font-medium"
|
:class="[textColorClass]"
|
||||||
|
@click="colorRef?.click()"
|
||||||
>
|
>
|
||||||
{{ t('label') }}
|
{{ t('label') }}
|
||||||
</label>
|
</UiButton>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
:id
|
:id
|
||||||
:readonly="read_only"
|
:readonly="read_only"
|
||||||
:disabled="read_only"
|
:disabled="read_only"
|
||||||
:title="t('title')"
|
:title="t('pick')"
|
||||||
class="p-0 cursor-pointer disabled:opacity-50 disabled:pointer-events-none w-14 h-10"
|
class="top-0 left-0 absolute size-0"
|
||||||
type="color"
|
type="color"
|
||||||
v-model="model"
|
v-model="model"
|
||||||
|
ref="colorRef"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<UiTooltip :tooltip="t('reset')">
|
||||||
@click="model = null"
|
<button
|
||||||
class="btn btn-sm text-sm"
|
@click="model = ''"
|
||||||
:class="{ 'btn-disabled': read_only }"
|
class="btn btn-sm text-sm btn-outline btn-error"
|
||||||
>
|
:class="{ 'btn-disabled': read_only }"
|
||||||
{{ t('reset') }}
|
type="button"
|
||||||
</button>
|
>
|
||||||
|
<Icon name="mdi:refresh" />
|
||||||
|
</button>
|
||||||
|
</UiTooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -31,11 +36,20 @@
|
|||||||
const id = useId()
|
const id = useId()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const model = defineModel()
|
const model = defineModel<string | null>()
|
||||||
|
const colorRef = useTemplateRef('colorRef')
|
||||||
defineProps({
|
defineProps({
|
||||||
read_only: Boolean,
|
read_only: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { currentTheme } = storeToRefs(useUiStore())
|
||||||
|
const textColorClass = computed(() => {
|
||||||
|
if (!model.value)
|
||||||
|
return currentTheme.value.value === 'dark' ? 'text-black' : 'text-white'
|
||||||
|
|
||||||
|
const color = getContrastingTextColor(model.value)
|
||||||
|
return color === 'white' ? 'text-white' : 'text-black'
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="json">
|
<i18n lang="json">
|
||||||
@ -43,12 +57,14 @@ defineProps({
|
|||||||
"de": {
|
"de": {
|
||||||
"label": "Farbauswahl",
|
"label": "Farbauswahl",
|
||||||
"title": "Wähle eine Farbe aus",
|
"title": "Wähle eine Farbe aus",
|
||||||
"reset": "zurücksetzen"
|
"reset": "zurücksetzen",
|
||||||
|
"pick": "Auswahl"
|
||||||
},
|
},
|
||||||
"en": {
|
"en": {
|
||||||
"label": "Color Picker",
|
"label": "Color Picker",
|
||||||
"title": "Choose a color",
|
"title": "Choose a color",
|
||||||
"reset": "Reset"
|
"reset": "Reset",
|
||||||
|
"pick": "Pick"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|||||||
@ -1,37 +1,77 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiSelect
|
<UiDropdown
|
||||||
v-model="icon"
|
:items="icons"
|
||||||
:options="icons"
|
class="btn"
|
||||||
label="Icon Picker"
|
@select="(newIcon) => (iconName = newIcon)"
|
||||||
>
|
>
|
||||||
<template #value="{ value }">
|
<template #activator>
|
||||||
<Icon
|
<Icon :name="iconName ? iconName : defaultIcon || icons.at(0)" />
|
||||||
:name="value"
|
|
||||||
v-if="value"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template #option="{ option }">
|
|
||||||
<Icon :name="option ?? ''" />
|
|
||||||
</template>
|
|
||||||
</UiSelect>
|
|
||||||
|
|
||||||
<UiDropdown :items="icons">
|
<template #items="{ items }">
|
||||||
<template #activator> {{ icons.find((_icon) => _icon === icon) }}</template>
|
<div class="grid grid-cols-6 -ml-2">
|
||||||
<template #item="{ item }">
|
<li
|
||||||
<Icon :name="`mdi:${item}`" />
|
class="dropdown-item"
|
||||||
{{ item }}
|
v-for="item in items"
|
||||||
|
@click="iconName = item"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
:name="item"
|
||||||
|
size="36"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UiDropdown>
|
</UiDropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const icons = [
|
const icons = [
|
||||||
'streamline:money-bank-institution-money-saving-bank-payment-finance',
|
'mdi:folder-outline',
|
||||||
'material-symbols:star-outline-rounded',
|
'mdi:key-outline',
|
||||||
'pepicons-pop:smartphone-home-button',
|
'pepicons-pop:smartphone-home-button',
|
||||||
'majesticons:desktop-computer-line',
|
'mdi:amazon',
|
||||||
'mdi:folder',
|
'proicons:bank',
|
||||||
|
'mdi:bitcoin',
|
||||||
|
'mdi:piggy-bank-outline',
|
||||||
|
'mdi:account-outline',
|
||||||
|
'proicons:computer',
|
||||||
|
'proicons:cloud',
|
||||||
|
'proicons:game',
|
||||||
|
'proicons:github',
|
||||||
|
'proicons:wrench',
|
||||||
|
'proicons:vehicle-car',
|
||||||
|
'proicons:wi-fi',
|
||||||
|
'meteor-icons:microchip',
|
||||||
|
'meteor-icons:headphones',
|
||||||
|
'meteor-icons:star',
|
||||||
|
'fe:mail',
|
||||||
|
'fe:rocket',
|
||||||
|
'fxemoji:trolleybus',
|
||||||
|
'mdi:folder-outline',
|
||||||
|
'mdi:key-outline',
|
||||||
|
'pepicons-pop:smartphone-home-button',
|
||||||
|
'mdi:amazon',
|
||||||
|
'proicons:bank',
|
||||||
|
'mdi:bitcoin',
|
||||||
|
'mdi:piggy-bank-outline',
|
||||||
|
'mdi:account-outline',
|
||||||
|
'proicons:computer',
|
||||||
|
'proicons:cloud',
|
||||||
|
'proicons:game',
|
||||||
|
'proicons:github',
|
||||||
|
'proicons:wrench',
|
||||||
|
'proicons:vehicle-car',
|
||||||
|
'proicons:wi-fi',
|
||||||
|
'meteor-icons:microchip',
|
||||||
|
'meteor-icons:headphones',
|
||||||
|
'meteor-icons:star',
|
||||||
|
'fe:mail',
|
||||||
|
'fe:rocket',
|
||||||
|
'fxemoji:trolleybus',
|
||||||
]
|
]
|
||||||
|
|
||||||
const icon = defineModel<string | undefined | null>({ default: '' })
|
const iconName = defineModel<string | undefined | null>()
|
||||||
|
|
||||||
|
defineProps<{ defaultIcon?: string }>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
:value
|
:value
|
||||||
class=""
|
class=""
|
||||||
>
|
>
|
||||||
<span>{{ value }}</span>
|
<span>{{ value ?? label }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|||||||
54
src/components/ui/textarea/index.vue
Normal file
54
src/components/ui/textarea/index.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative">
|
||||||
|
<UiButton
|
||||||
|
v-if="withCopyButton"
|
||||||
|
:tooltip="t('copy')"
|
||||||
|
class="btn-square btn-outline btn-accent absolute z-10 top-2 right-2"
|
||||||
|
@click="copy(`${value}`)"
|
||||||
|
>
|
||||||
|
<Icon :name="copied ? 'mdi:check' : 'mdi:content-copy'" />
|
||||||
|
</UiButton>
|
||||||
|
|
||||||
|
<div class="textarea-floating">
|
||||||
|
<textarea
|
||||||
|
:class="{ 'pr-10': withCopyButton }"
|
||||||
|
:id
|
||||||
|
:placeholder
|
||||||
|
:readonly="read_only"
|
||||||
|
class="textarea"
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-model="value"
|
||||||
|
></textarea>
|
||||||
|
<label
|
||||||
|
class="textarea-floating-label"
|
||||||
|
:for="id"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
placeholder?: string
|
||||||
|
label?: string
|
||||||
|
read_only?: boolean
|
||||||
|
withCopyButton?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const id = useId()
|
||||||
|
|
||||||
|
const value = defineModel<string | null | undefined>()
|
||||||
|
|
||||||
|
const { copy, copied } = useClipboard()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
copy: Kopieren
|
||||||
|
en:
|
||||||
|
copy: Copy
|
||||||
|
</i18n>
|
||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="tooltip [--prevent-popper:false]">
|
<div class="tooltip [--prevent-popper:false]">
|
||||||
<div
|
<div
|
||||||
class="tooltip-toggle"
|
class="tooltip-toggle"
|
||||||
aria-label="Tooltip"
|
:aria-label="tooltip"
|
||||||
>
|
>
|
||||||
<slot>
|
<slot>
|
||||||
<button class="btn btn-square">
|
<button class="btn btn-square">
|
||||||
@ -11,7 +11,7 @@
|
|||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40"
|
class="tooltip-content tooltip-shown:opacity-100 tooltip-shown:visible z-40 pointer-events-none"
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -1,15 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiDialog
|
<UiDialogConfirm
|
||||||
v-model:open="open"
|
v-model:open="open"
|
||||||
:title="t('title')"
|
|
||||||
class="btn btn-primary btn-outline shadow-md btn-lg"
|
class="btn btn-primary btn-outline shadow-md btn-lg"
|
||||||
@click="open = true"
|
@click="open = true"
|
||||||
|
@abort="open = false"
|
||||||
|
@confirm="onCreateAsync"
|
||||||
|
:confirm-label="t('create')"
|
||||||
>
|
>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<Icon name="mdi:plus" />
|
<Icon name="mdi:plus" />
|
||||||
{{ t('database.create') }}
|
{{ t('database.create') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #title>
|
||||||
|
<div class="flex gap-x-2 items-center">
|
||||||
|
<Icon
|
||||||
|
name="mdi:safe"
|
||||||
|
class="text-primary"
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
{{ t('title') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<form
|
<form
|
||||||
class="flex flex-col gap-4"
|
class="flex flex-col gap-4"
|
||||||
@submit="onCreateAsync"
|
@submit="onCreateAsync"
|
||||||
@ -32,11 +45,12 @@
|
|||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<template #buttons>
|
<!-- <template #buttons>
|
||||||
<UiButton
|
<UiButton
|
||||||
class="btn-error w-full sm:w-auto"
|
class="btn-error btn-outline w-full sm:w-auto"
|
||||||
@click="onClose"
|
@click="onClose"
|
||||||
>
|
>
|
||||||
|
<Icon name="mdi:x" />
|
||||||
{{ t('abort') }}
|
{{ t('abort') }}
|
||||||
</UiButton>
|
</UiButton>
|
||||||
|
|
||||||
@ -46,8 +60,8 @@
|
|||||||
>
|
>
|
||||||
{{ t('create') }}
|
{{ t('create') }}
|
||||||
</UiButton>
|
</UiButton>
|
||||||
</template>
|
</template> -->
|
||||||
</UiDialog>
|
</UiDialogConfirm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -151,7 +165,7 @@ const onClose = () => {
|
|||||||
"create": "Neue Vault anlegen",
|
"create": "Neue Vault anlegen",
|
||||||
"name": "HaexVault"
|
"name": "HaexVault"
|
||||||
},
|
},
|
||||||
"title": "Neue Datenbank anlegen",
|
"title": "Neue Vault anlegen",
|
||||||
"create": "Erstellen",
|
"create": "Erstellen",
|
||||||
"abort": "Abbrechen",
|
"abort": "Abbrechen",
|
||||||
"description": "Haex Vault für deine geheimsten Geheimnisse"
|
"description": "Haex Vault für deine geheimsten Geheimnisse"
|
||||||
@ -163,7 +177,7 @@ const onClose = () => {
|
|||||||
"create": "Create new Vault",
|
"create": "Create new Vault",
|
||||||
"name": "HaexVault"
|
"name": "HaexVault"
|
||||||
},
|
},
|
||||||
"title": "Create New Database",
|
"title": "Create New Vault",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"abort": "Abort",
|
"abort": "Abort",
|
||||||
"description": "Haex Vault for your most secret secrets"
|
"description": "Haex Vault for your most secret secrets"
|
||||||
|
|||||||
@ -78,7 +78,7 @@ const { add } = useSnackbar()
|
|||||||
const handleError = (error: unknown) => {
|
const handleError = (error: unknown) => {
|
||||||
open.value = false
|
open.value = false
|
||||||
console.error('handleError', error, typeof error)
|
console.error('handleError', error, typeof error)
|
||||||
add({ type: 'error', text: JSON.stringify(error) })
|
add({ type: 'error', text: `${error}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { openAsync } = useVaultStore()
|
const { openAsync } = useVaultStore()
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full w-full flex flex-col overflow-hidden">
|
<div class="h-full w-full flex flex-col overflow-hidden">
|
||||||
<nav
|
<nav
|
||||||
class="navbar bg-base-100 rounded-b max-sm:shadow border-b border-base-content/25 sm:z-20 relative px-2 py-0 sm:py-2"
|
class="navbar rounded-b max-sm:shadow border-b border-base-content/25 sm:z-20 relative px-2 py-0 sm:py-2"
|
||||||
>
|
>
|
||||||
<UiTooltip :tooltip="isVisible ? t('sidebar.close') : t('sidebar.show')">
|
<UiTooltip :tooltip="isVisible ? t('sidebar.close') : t('sidebar.show')">
|
||||||
<button
|
<button
|
||||||
@ -35,82 +35,8 @@
|
|||||||
</NuxtLinkLocale>
|
</NuxtLinkLocale>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-end flex items-center gap-4 me-4">
|
<div class="flex items-center gap-4 me-4">
|
||||||
<div
|
<HaexMenuNotifications />
|
||||||
class="dropdown relative inline-flex [--auto-close:inside] [--offset:18] [--placement:bottom]"
|
|
||||||
>
|
|
||||||
<UiTooltip :tooltip="t('notifications.label')">
|
|
||||||
<button
|
|
||||||
id="dropdown-scrollable"
|
|
||||||
type="button"
|
|
||||||
class="dropdown-toggle btn btn-text btn-circle dropdown-open:bg-base-content/10 size-10"
|
|
||||||
aria-haspopup="menu"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-label="Dropdown"
|
|
||||||
>
|
|
||||||
<div class="indicator">
|
|
||||||
<span
|
|
||||||
v-show="notifications.length"
|
|
||||||
class="indicator-item bg-error size-2 rounded-full text-sm"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="icon-[tabler--bell] text-base-content size-[1.375rem]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</UiTooltip>
|
|
||||||
<div
|
|
||||||
class="dropdown-menu dropdown-open:opacity-100 hidden w-full max-w-96 shadow"
|
|
||||||
role="menu"
|
|
||||||
aria-orientation="vertical"
|
|
||||||
aria-labelledby="dropdown-scrollable"
|
|
||||||
>
|
|
||||||
<div class="dropdown-header justify-center">
|
|
||||||
<h6 class="text-base-content text-base">
|
|
||||||
{{ t('notifications.label') }}
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="vertical-scrollbar horizontal-scrollbar rounded-scrollbar text-base-content/80 max-h-56 overflow-auto"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="notification in notifications"
|
|
||||||
:key="notification.id"
|
|
||||||
class="dropdown-item"
|
|
||||||
>
|
|
||||||
<div class="avatar">
|
|
||||||
<div class="w-10 rounded-full">
|
|
||||||
<img
|
|
||||||
v-if="notification.image"
|
|
||||||
:src="notification.image"
|
|
||||||
:alt="notification.alt ?? 'notification avatar'"
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
v-else-if="notification.icon"
|
|
||||||
:name="notification.icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-60">
|
|
||||||
<h6 class="truncate text-base">
|
|
||||||
{{ notification.title }}
|
|
||||||
</h6>
|
|
||||||
<small class="text-base-content/50 truncate">
|
|
||||||
{{ notification.text }}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<NuxtLinkLocale
|
|
||||||
:to="{ name: 'notifications' }"
|
|
||||||
class="dropdown-footer justify-center gap-1 hover:bg-base-content/10"
|
|
||||||
>
|
|
||||||
<span class="icon-[tabler--eye] size-4" />
|
|
||||||
{{ t('notifications.view_all') }}
|
|
||||||
</NuxtLinkLocale>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<HaexMenuMain />
|
<HaexMenuMain />
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
@ -154,8 +80,6 @@ const { currentVaultName } = storeToRefs(useVaultStore())
|
|||||||
|
|
||||||
const { menu, isVisible } = storeToRefs(useSidebarStore())
|
const { menu, isVisible } = storeToRefs(useSidebarStore())
|
||||||
|
|
||||||
const { notifications } = storeToRefs(useNotificationStore())
|
|
||||||
|
|
||||||
const { extensionLinks } = storeToRefs(useExtensionsStore())
|
const { extensionLinks } = storeToRefs(useExtensionsStore())
|
||||||
|
|
||||||
const toogleSidebar = () => {
|
const toogleSidebar = () => {
|
||||||
@ -165,18 +89,12 @@ const toogleSidebar = () => {
|
|||||||
|
|
||||||
<i18n lang="yaml">
|
<i18n lang="yaml">
|
||||||
de:
|
de:
|
||||||
notifications:
|
|
||||||
label: Benachrichtigungen
|
|
||||||
view_all: Alle ansehen
|
|
||||||
vault:
|
vault:
|
||||||
close: Vault schließen
|
close: Vault schließen
|
||||||
sidebar:
|
sidebar:
|
||||||
close: Sidebar ausblenden
|
close: Sidebar ausblenden
|
||||||
show: Sidebar anzeigen
|
show: Sidebar anzeigen
|
||||||
en:
|
en:
|
||||||
notifications:
|
|
||||||
label: Notifications
|
|
||||||
view_all: View all
|
|
||||||
vault:
|
vault:
|
||||||
close: Close vault
|
close: Close vault
|
||||||
sidebar:
|
sidebar:
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-4">
|
<div class="p-2 h-full">
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -8,6 +8,4 @@
|
|||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'passwords',
|
name: 'passwords',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,78 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VaultCard
|
<HaexPassGroup
|
||||||
:title="t('title')"
|
v-model="group"
|
||||||
icon="mdi:folder-plus-outline"
|
mode="create"
|
||||||
@close="onClose"
|
@close="onClose"
|
||||||
>
|
@submit="createAsync"
|
||||||
<div
|
/>
|
||||||
class="flex flex-col gap-4 w-full p-4"
|
|
||||||
@keyup.enter="onCreate"
|
|
||||||
>
|
|
||||||
<UiInput
|
|
||||||
:check-input="check"
|
|
||||||
:label="t('name.label')"
|
|
||||||
:placeholder="t('name.label')"
|
|
||||||
autofocus
|
|
||||||
v-model:errors="errors.name"
|
|
||||||
v-model="vaultGroup.name"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UiInput
|
|
||||||
v-model="vaultGroup.description"
|
|
||||||
:check-input="check"
|
|
||||||
:label="t('description.label')"
|
|
||||||
:placeholder="t('description.label')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UiSelectColor v-model="vaultGroup.color" />
|
|
||||||
|
|
||||||
{{ vaultGroup.icon }}
|
|
||||||
<UiSelectIcon v-model="vaultGroup.icon" />
|
|
||||||
|
|
||||||
<div class="flex flex-wrap justify-end gap-4">
|
|
||||||
<button
|
|
||||||
class="btn btn-error btn-outline flex-1 flex-nowrap"
|
|
||||||
@click="onClose"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{{ t('abort') }}
|
|
||||||
<Icon name="mdi:close" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary flex-1 flex-nowrap"
|
|
||||||
type="button"
|
|
||||||
@click="onCreate"
|
|
||||||
>
|
|
||||||
{{ t('create') }}
|
|
||||||
<Icon name="mdi:check" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VaultCard>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { InsertHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'passwordGroupCreate',
|
name: 'passwordGroupCreate',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const check = ref(false)
|
|
||||||
|
|
||||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||||
const vaultGroup = ref<InsertHaexPasswordsGroups>({
|
const group = ref<SelectHaexPasswordsGroups>({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
id: '',
|
id: '',
|
||||||
color: null,
|
color: null,
|
||||||
icon: null,
|
icon: null,
|
||||||
order: null,
|
order: null,
|
||||||
parentId: currentGroupId.value,
|
parentId: currentGroupId.value || null,
|
||||||
|
createdAt: null,
|
||||||
|
updateAt: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const errors = ref({
|
const errors = ref({
|
||||||
@ -84,23 +38,18 @@ const onClose = () => {
|
|||||||
useRouter().back()
|
useRouter().back()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCreate = async () => {
|
const { addGroupAsync } = usePasswordGroupStore()
|
||||||
|
const createAsync = async () => {
|
||||||
try {
|
try {
|
||||||
check.value = true
|
|
||||||
|
|
||||||
if (errors.value.name.length || errors.value.description.length) return
|
if (errors.value.name.length || errors.value.description.length) return
|
||||||
|
|
||||||
const { addGroupAsync } = usePasswordGroupStore()
|
const newGroup = await addGroupAsync(group.value)
|
||||||
|
|
||||||
const newGroup = await addGroupAsync(vaultGroup.value)
|
|
||||||
|
|
||||||
console.log('newGroup', newGroup)
|
console.log('newGroup', newGroup)
|
||||||
if (!newGroup.id) {
|
if (!newGroup.id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//console.log('created group with id', newGroup?.id)
|
|
||||||
|
|
||||||
//currentGroupId.value = newGroup?.id
|
|
||||||
await navigateTo(
|
await navigateTo(
|
||||||
useLocalePath()({
|
useLocalePath()({
|
||||||
name: 'passwordGroupItems',
|
name: 'passwordGroupItems',
|
||||||
@ -117,31 +66,3 @@ const onCreate = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="json">
|
|
||||||
{
|
|
||||||
"de": {
|
|
||||||
"title": "Neue Gruppe anlegen",
|
|
||||||
"abort": "Abbrechen",
|
|
||||||
"create": "Anlegen",
|
|
||||||
"name": {
|
|
||||||
"label": "Name"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"label": "Beschreibung"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"en": {
|
|
||||||
"title": "Create new Group",
|
|
||||||
"abort": "Abort",
|
|
||||||
"create": "Create",
|
|
||||||
"name": {
|
|
||||||
"label": "Name"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"label": "Description"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</i18n>
|
|
||||||
|
|||||||
@ -1,150 +1,85 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VaultGroup
|
<HaexPassGroup
|
||||||
|
v-model="currentGroup"
|
||||||
|
mode="edit"
|
||||||
|
@close="onClose"
|
||||||
@submit="onSaveAsync"
|
@submit="onSaveAsync"
|
||||||
@back="onBackAsync"
|
/>
|
||||||
@reject="onRejectAsync"
|
|
||||||
@close="onCloseAsync"
|
|
||||||
v-model="vaultGroup"
|
|
||||||
v-model:read_only="read_only"
|
|
||||||
:originally
|
|
||||||
>
|
|
||||||
<!-- <template #bottom="{ onSubmit, onClose }">
|
|
||||||
<button
|
|
||||||
class="btn btn-error flex-1 flex-nowrap"
|
|
||||||
@click="onClose"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{{ t('abort') }}
|
|
||||||
<Icon name="mdi:close" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-primary flex-1 flex-nowrap"
|
|
||||||
type="button"
|
|
||||||
@click="onSubmit"
|
|
||||||
>
|
|
||||||
{{ t('save') }}
|
|
||||||
<Icon name="mdi:check" />
|
|
||||||
</button>
|
|
||||||
</template> -->
|
|
||||||
</VaultGroup>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
|
|
||||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'passwordGroupEdit',
|
name: 'passwordGroupEdit',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { read_only } = storeToRefs(useVaultStore())
|
const { t } = useI18n()
|
||||||
|
|
||||||
const vaultGroup = ref<SelectHaexPasswordsGroups>({
|
const check = ref(false)
|
||||||
color: '',
|
|
||||||
description: '',
|
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||||
icon: '',
|
|
||||||
id: '',
|
//const group = computed(() => currentGroup.value)
|
||||||
name: '',
|
|
||||||
order: null,
|
const errors = ref({
|
||||||
parentId: '',
|
name: [],
|
||||||
createdAt: null,
|
description: [],
|
||||||
updateAt: null,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const originally = ref<SelectHaexPasswordsGroups>()
|
const onClose = () => {
|
||||||
|
useRouter().back()
|
||||||
const onCloseAsync = async () => {
|
|
||||||
if (read_only.value) return navigateToGroupItemsAsync(vaultGroup.value.id)
|
|
||||||
else read_only.value = true
|
|
||||||
}
|
}
|
||||||
/* {
|
|
||||||
await navigateTo(
|
|
||||||
useLocaleRoute()({
|
|
||||||
name: 'vaultGroupEntries',
|
|
||||||
params: {
|
|
||||||
...useRouter().currentRoute.value.params,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
...useRouter().currentRoute.value.query,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}; */
|
|
||||||
|
|
||||||
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
|
||||||
const { readGroupAsync, navigateToGroupItemsAsync } = usePasswordGroupStore()
|
|
||||||
|
|
||||||
const getGroupAsync = async () => {
|
|
||||||
if (!currentGroupId.value) return
|
|
||||||
|
|
||||||
const group = await readGroupAsync(currentGroupId.value)
|
|
||||||
console.log('found group', group)
|
|
||||||
if (group) {
|
|
||||||
vaultGroup.value = group
|
|
||||||
originally.value = { ...group }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
watch(currentGroupId, async () => getGroupAsync(), { immediate: true })
|
|
||||||
|
|
||||||
const { add } = useSnackbar()
|
const { add } = useSnackbar()
|
||||||
|
|
||||||
const onSaveAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
const onSaveAsync = async () => {
|
||||||
try {
|
try {
|
||||||
|
check.value = true
|
||||||
|
if (!currentGroup.value) return
|
||||||
|
|
||||||
|
console.log('onSave', errors.value)
|
||||||
|
if (errors.value.name.length || errors.value.description.length) return
|
||||||
|
|
||||||
const { updateAsync } = usePasswordGroupStore()
|
const { updateAsync } = usePasswordGroupStore()
|
||||||
await updateAsync(vaultGroup.value)
|
|
||||||
await getGroupAsync()
|
await updateAsync(currentGroup.value)
|
||||||
read_only.value = true
|
|
||||||
if (to) {
|
add({ type: 'success', text: t('change.success') })
|
||||||
return navigateTo(to)
|
onClose()
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
add({
|
add({ type: 'error', text: t('change.error') })
|
||||||
type: 'error',
|
|
||||||
text: JSON.stringify(error),
|
|
||||||
})
|
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onBackAsync = async () => {
|
|
||||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
|
||||||
await navigateToGroupItemsAsync(vaultGroup.value.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRejectAsync = async (to?: RouteLocationNormalizedLoadedGeneric) => {
|
|
||||||
if (originally.value) vaultGroup.value = { ...originally.value }
|
|
||||||
if (to) return navigateTo(to)
|
|
||||||
else return onBackAsync
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<i18n lang="json">
|
<i18n lang="yaml">
|
||||||
{
|
de:
|
||||||
"de": {
|
title: Gruppe ändern
|
||||||
"title": "Gruppe anpassen",
|
abort: Abbrechen
|
||||||
"abort": "Abbrechen",
|
save: Speichern
|
||||||
"save": "Speichern",
|
name:
|
||||||
"name": {
|
label: Name
|
||||||
"label": "Name"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"label": "Beschreibung"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"en": {
|
description:
|
||||||
"title": "Edit Group",
|
label: Beschreibung
|
||||||
"abort": "Abort",
|
|
||||||
"save": "Save",
|
change:
|
||||||
"name": {
|
success: Änderung erfolgreich gespeichert
|
||||||
"label": "Name"
|
error: Änderung konnte nicht gespeichert werden
|
||||||
},
|
|
||||||
"description": {
|
en:
|
||||||
"label": "Description"
|
title: Edit Group
|
||||||
}
|
abort: Abort
|
||||||
}
|
save: Save
|
||||||
}
|
name:
|
||||||
|
label: Name
|
||||||
|
|
||||||
|
description:
|
||||||
|
label: Description
|
||||||
|
|
||||||
|
change:
|
||||||
|
success: Change successfully saved
|
||||||
|
error: Change could not be saved
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|||||||
@ -1,40 +1,228 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative h-full">
|
<div class="relative h-full">
|
||||||
<div>
|
<div class="h-full">
|
||||||
<HaexPassMobileMenu :group-items="currentGroupItems" />
|
<div class="h-full overflow-auto p-1 flex flex-col">
|
||||||
|
<HaexPassGroupBreadcrumbs
|
||||||
|
:items="breadCrumbs"
|
||||||
|
v-show="breadCrumbs.length"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HaexPassMobileMenu
|
||||||
|
ref="listRef"
|
||||||
|
:menu-items="groupItems"
|
||||||
|
v-model:selected-items="selectedItems"
|
||||||
|
sel
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="fixed bottom-4 flex justify-center w-full transition-all pointer-events-none"
|
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end"
|
||||||
:class="[isVisible ? 'left-15' : 'left-0']"
|
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||||
>
|
>
|
||||||
<!-- <UiButton class="btn btn-primary btn-lg btn-square rotate-45">
|
<div class="w-full pl-8"></div>
|
||||||
<Icon name="mdi:plus" />
|
|
||||||
</UiButton> -->
|
|
||||||
<UiButtonAction
|
<UiButtonAction
|
||||||
class="pointer-events-auto"
|
|
||||||
:menu
|
:menu
|
||||||
|
v-if="!inTrashGroup"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-col sm:flex-row gap-4 w-full justify-end items-end pr-8"
|
||||||
|
>
|
||||||
|
<UiButton
|
||||||
|
v-show="selectedItems.size === 1"
|
||||||
|
class="btn-square btn-accent"
|
||||||
|
@click="onEditAsync"
|
||||||
|
:tooltip="t('edit')"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:pencil" />
|
||||||
|
</UiButton>
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
class="btn-square btn-accent"
|
||||||
|
v-show="selectedItems.size"
|
||||||
|
@click="onCut"
|
||||||
|
:tooltip="t('cut')"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:scissors" />
|
||||||
|
</UiButton>
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
class="btn-square btn-accent"
|
||||||
|
v-show="selectedGroupItems?.length"
|
||||||
|
@click="onPasteAsync"
|
||||||
|
:tooltip="t('paste')"
|
||||||
|
>
|
||||||
|
<Icon name="proicons:clipboard-paste" />
|
||||||
|
</UiButton>
|
||||||
|
|
||||||
|
<UiButton
|
||||||
|
v-show="selectedItems.size"
|
||||||
|
class="btn-square btn-accent"
|
||||||
|
@click="onDeleteAsync"
|
||||||
|
:tooltip="t('delete')"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:trash" />
|
||||||
|
</UiButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SelectHaexPasswordsItems } from '~~/src-tauri/database/schemas/vault'
|
import type { IPasswordMenuItem } from '~/components/haex/pass/mobile/menu/types'
|
||||||
|
import { useMagicKeys } from '@vueuse/core'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'passwordGroupItems',
|
name: 'passwordGroupItems',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const selectedItems = ref<Set<IPasswordMenuItem>>(new Set())
|
||||||
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
const { menu } = storeToRefs(usePasswordsActionMenuStore())
|
||||||
const items = ref<SelectHaexPasswordsItems[]>([])
|
|
||||||
|
|
||||||
const { readGroupItemsAsync } = usePasswordGroupStore()
|
const {
|
||||||
const { currentGroupItems } = storeToRefs(usePasswordGroupStore())
|
currentGroupItems,
|
||||||
|
breadCrumbs,
|
||||||
|
selectedGroupItems,
|
||||||
|
currentGroupId,
|
||||||
|
inTrashGroup,
|
||||||
|
} = storeToRefs(usePasswordGroupStore())
|
||||||
|
const { insertGroupItemsAsync } = usePasswordGroupStore()
|
||||||
|
|
||||||
|
const groupItems = computed<IPasswordMenuItem[]>(() => {
|
||||||
|
const items: IPasswordMenuItem[] = []
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
...currentGroupItems.value.groups.map<IPasswordMenuItem>((group) => ({
|
||||||
|
name: group.name,
|
||||||
|
id: group.id,
|
||||||
|
icon: group.icon,
|
||||||
|
type: 'group',
|
||||||
|
color: group.color,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
|
items.push(
|
||||||
|
...currentGroupItems.value.items.map<IPasswordMenuItem>((item) => ({
|
||||||
|
name: item.title,
|
||||||
|
id: item.id,
|
||||||
|
icon: item.icon,
|
||||||
|
type: 'item',
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
return items
|
||||||
|
})
|
||||||
|
|
||||||
const { isVisible } = storeToRefs(useSidebarStore())
|
const { isVisible } = storeToRefs(useSidebarStore())
|
||||||
|
|
||||||
console.log('currentGroupItems', currentGroupItems.value)
|
const onEditAsync = async () => {
|
||||||
const test = () => console.log('currentGroupItems', currentGroupItems.value)
|
const item = selectedItems.value.values().next().value
|
||||||
|
console.log('onEditAsync', item)
|
||||||
|
if (item?.type === 'group')
|
||||||
|
await navigateTo(
|
||||||
|
useLocalePath()({
|
||||||
|
name: 'passwordGroupEdit',
|
||||||
|
params: { groupId: item.id },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
else if (item?.type === 'item') {
|
||||||
|
await navigateTo(
|
||||||
|
useLocalePath()({
|
||||||
|
name: 'passwordItemEdit',
|
||||||
|
params: { itemId: item.id },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onKeyStroke('e', async (e) => {
|
||||||
|
if (e.ctrlKey) {
|
||||||
|
await onEditAsync()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(async () => {})
|
const onCut = () => {
|
||||||
|
selectedGroupItems.value = [...selectedItems.value]
|
||||||
|
selectedItems.value.clear()
|
||||||
|
}
|
||||||
|
onKeyStroke('x', (event) => {
|
||||||
|
if (event.ctrlKey && selectedItems.value.size) {
|
||||||
|
event.preventDefault()
|
||||||
|
onCut()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { add } = useSnackbar()
|
||||||
|
|
||||||
|
const onPasteAsync = async () => {
|
||||||
|
if (!selectedGroupItems.value?.length) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await insertGroupItemsAsync(
|
||||||
|
[...selectedGroupItems.value],
|
||||||
|
currentGroupId.value,
|
||||||
|
)
|
||||||
|
await syncGroupItemsAsync(currentGroupId.value)
|
||||||
|
selectedGroupItems.value = []
|
||||||
|
selectedItems.value.clear()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
selectedGroupItems.value = []
|
||||||
|
add({ type: 'error', text: t('error.paste') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onKeyStroke('v', async (event) => {
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
event.preventDefault()
|
||||||
|
await onPasteAsync()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { escape } = useMagicKeys()
|
||||||
|
watch(escape, () => {
|
||||||
|
selectedItems.value.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
onKeyStroke('a', (event) => {
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
event.preventDefault()
|
||||||
|
selectedItems.value = new Set(groupItems.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { deleteAsync } = usePasswordItemStore()
|
||||||
|
const { deleteGroupAsync, syncGroupItemsAsync } = usePasswordGroupStore()
|
||||||
|
const onDeleteAsync = async () => {
|
||||||
|
for (const item of selectedItems.value) {
|
||||||
|
if (item.type === 'group') {
|
||||||
|
await deleteGroupAsync(item.id, inTrashGroup.value)
|
||||||
|
}
|
||||||
|
if (item.type === 'item') {
|
||||||
|
await deleteAsync(item.id, inTrashGroup.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedItems.value.clear()
|
||||||
|
await syncGroupItemsAsync(currentGroupId.value)
|
||||||
|
}
|
||||||
|
const keys = useMagicKeys()
|
||||||
|
watch(keys.delete, async () => {
|
||||||
|
await onDeleteAsync()
|
||||||
|
})
|
||||||
|
|
||||||
|
const listRef = useTemplateRef<HTMLElement>('listRef')
|
||||||
|
onClickOutside(listRef, () => setTimeout(() => selectedItems.value.clear(), 50))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
cut: Ausschneiden
|
||||||
|
paste: Einfügen
|
||||||
|
delete: Löschen
|
||||||
|
edit: Bearbeiten
|
||||||
|
en:
|
||||||
|
cut: Cut
|
||||||
|
paste: Paste
|
||||||
|
delete: Delete
|
||||||
|
edit: Edit
|
||||||
|
</i18n>
|
||||||
|
|||||||
@ -1,3 +1,297 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>item</div>
|
<div>
|
||||||
|
<HaexPassItem
|
||||||
|
:history="item.history"
|
||||||
|
:read_only
|
||||||
|
@close="onClose"
|
||||||
|
v-model:details="item.details"
|
||||||
|
v-model:key-values-add="item.keyValuesAdd"
|
||||||
|
v-model:key-values-delete="item.keyValuesDelete"
|
||||||
|
v-model:key-values="item.keyValues"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end"
|
||||||
|
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-center w-full">
|
||||||
|
<UiTooltip :tooltip="t('abort')">
|
||||||
|
<UiButton
|
||||||
|
class="btn-accent btn-square"
|
||||||
|
@click="onClose"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:close" />
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UiTooltip
|
||||||
|
v-show="read_only && !hasChanges"
|
||||||
|
:tooltip="t('edit')"
|
||||||
|
>
|
||||||
|
<UiButton
|
||||||
|
class="btn-xl btn-square btn-primary"
|
||||||
|
@click="read_only = false"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:pencil-outline"
|
||||||
|
class="size-11 shrink-0"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
|
||||||
|
<UiTooltip
|
||||||
|
v-show="!read_only && !hasChanges"
|
||||||
|
:tooltip="t('noEdit')"
|
||||||
|
>
|
||||||
|
<UiButton
|
||||||
|
class="btn-xl btn-square btn-primary"
|
||||||
|
@click="read_only = true"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:pencil-off-outline"
|
||||||
|
class="size-11 shrink-0"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
|
||||||
|
<UiTooltip
|
||||||
|
:tooltip="t('save')"
|
||||||
|
v-show="!read_only && hasChanges"
|
||||||
|
>
|
||||||
|
<UiButton
|
||||||
|
class="btn-xl btn-square btn-primary motion-duration-2000"
|
||||||
|
:class="{ 'motion-preset-pulse-sm': hasChanges }"
|
||||||
|
@click="onUpdateAsync"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:content-save-outline"
|
||||||
|
class="size-11 shrink-0"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center w-full">
|
||||||
|
<UiTooltip :tooltip="t('delete')">
|
||||||
|
<UiButton
|
||||||
|
class="btn-square btn-error"
|
||||||
|
@click="showConfirmDeleteDialog = true"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:trash-outline"
|
||||||
|
class="shrink-0"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UiDialogConfirm
|
||||||
|
v-model:open="showConfirmDeleteDialog"
|
||||||
|
:confirm-label="t('dialog.delete.label')"
|
||||||
|
:title="t('dialog.delete.title')"
|
||||||
|
@abort="showConfirmDeleteDialog = false"
|
||||||
|
@confirm="deleteItemAsync"
|
||||||
|
>
|
||||||
|
{{ t('dialog.delete.question') }}
|
||||||
|
</UiDialogConfirm>
|
||||||
|
|
||||||
|
<UiDialogConfirm
|
||||||
|
v-model:open="showUnsavedChangesDialog"
|
||||||
|
:confirm-label="t('dialog.unsavedChanges.label')"
|
||||||
|
:title="t('dialog.unsavedChanges.title')"
|
||||||
|
@abort="showUnsavedChangesDialog = false"
|
||||||
|
@confirm="onConfirmIgnoreChanges"
|
||||||
|
>
|
||||||
|
{{ t('dialog.unsavedChanges.question') }}
|
||||||
|
</UiDialogConfirm>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
SelectHaexPasswordsItemDetails,
|
||||||
|
SelectHaexPasswordsItemHistory,
|
||||||
|
SelectHaexPasswordsItemKeyValues,
|
||||||
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
name: 'passwordItemEdit',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
icon: String,
|
||||||
|
title: String,
|
||||||
|
withCopyButton: Boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { isVisible } = storeToRefs(useSidebarStore())
|
||||||
|
const read_only = ref(true)
|
||||||
|
const showConfirmDeleteDialog = ref(false)
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const item = reactive<{
|
||||||
|
details: SelectHaexPasswordsItemDetails
|
||||||
|
history: SelectHaexPasswordsItemHistory[]
|
||||||
|
keyValues: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
originalDetails: string | null
|
||||||
|
originalKeyValues: string | null
|
||||||
|
}>({
|
||||||
|
details: {
|
||||||
|
id: '',
|
||||||
|
createdAt: null,
|
||||||
|
icon: null,
|
||||||
|
note: null,
|
||||||
|
password: null,
|
||||||
|
tags: null,
|
||||||
|
title: null,
|
||||||
|
updateAt: null,
|
||||||
|
url: null,
|
||||||
|
username: null,
|
||||||
|
},
|
||||||
|
keyValues: [],
|
||||||
|
history: [],
|
||||||
|
keyValuesAdd: [],
|
||||||
|
keyValuesDelete: [],
|
||||||
|
originalDetails: null,
|
||||||
|
originalKeyValues: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { currentItem } = storeToRefs(usePasswordItemStore())
|
||||||
|
|
||||||
|
watch(currentItem, () => {
|
||||||
|
item.details = JSON.parse(JSON.stringify(currentItem.value?.details))
|
||||||
|
item.keyValues = JSON.parse(JSON.stringify(currentItem.value?.keyValues))
|
||||||
|
item.history = JSON.parse(JSON.stringify(currentItem.value?.history))
|
||||||
|
item.keyValuesAdd = []
|
||||||
|
item.keyValuesDelete = []
|
||||||
|
item.originalDetails = JSON.stringify(currentItem.value?.details)
|
||||||
|
item.originalKeyValues = JSON.stringify(currentItem.value?.keyValues)
|
||||||
|
ignoreChanges.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
const { add } = useSnackbar()
|
||||||
|
const { deleteAsync, updateAsync } = usePasswordItemStore()
|
||||||
|
const { syncGroupItemsAsync, trashId } = usePasswordGroupStore()
|
||||||
|
const { currentGroupId } = storeToRefs(usePasswordGroupStore())
|
||||||
|
|
||||||
|
const onUpdateAsync = async () => {
|
||||||
|
try {
|
||||||
|
const newId = await updateAsync({
|
||||||
|
details: item.details,
|
||||||
|
groupId: currentGroupId.value || null,
|
||||||
|
keyValues: item.keyValues,
|
||||||
|
keyValuesAdd: item.keyValuesAdd,
|
||||||
|
keyValuesDelete: item.keyValuesDelete,
|
||||||
|
})
|
||||||
|
if (newId) add({ type: 'success', text: t('success.update') })
|
||||||
|
syncGroupItemsAsync(currentGroupId.value)
|
||||||
|
ignoreChanges.value = true
|
||||||
|
onClose()
|
||||||
|
} catch (error) {
|
||||||
|
add({ type: 'error', text: t('error.update') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
if (showConfirmDeleteDialog.value || showUnsavedChangesDialog.value) return
|
||||||
|
|
||||||
|
if (hasChanges.value && !ignoreChanges.value)
|
||||||
|
return (showUnsavedChangesDialog.value = true)
|
||||||
|
|
||||||
|
ignoreChanges.value = false
|
||||||
|
read_only.value = true
|
||||||
|
useRouter().back()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteItemAsync = async () => {
|
||||||
|
try {
|
||||||
|
await deleteAsync(item.details.id, currentGroupId.value === trashId)
|
||||||
|
showConfirmDeleteDialog.value = false
|
||||||
|
add({ type: 'success', text: t('success.delete') })
|
||||||
|
syncGroupItemsAsync(currentGroupId.value)
|
||||||
|
onClose()
|
||||||
|
} catch (errro) {
|
||||||
|
add({
|
||||||
|
type: 'error',
|
||||||
|
text: t('error.delete'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChanges = computed(
|
||||||
|
() =>
|
||||||
|
!!(
|
||||||
|
item.originalDetails !== JSON.stringify(item.details) ||
|
||||||
|
item.originalKeyValues !== JSON.stringify(item.keyValues) ||
|
||||||
|
item.keyValuesAdd.length ||
|
||||||
|
item.keyValuesDelete.length
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
const showUnsavedChangesDialog = ref(false)
|
||||||
|
const ignoreChanges = ref(false)
|
||||||
|
|
||||||
|
const onConfirmIgnoreChanges = () => {
|
||||||
|
ignoreChanges.value = true
|
||||||
|
showUnsavedChangesDialog.value = false
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
save: Speichern
|
||||||
|
abort: Abbrechen
|
||||||
|
edit: Bearbeiten
|
||||||
|
noEdit: Lesemodus
|
||||||
|
delete: Löschen
|
||||||
|
success:
|
||||||
|
update: Eintrag erfolgreich aktualisiert
|
||||||
|
delete: Eintrag wurde gelöscht
|
||||||
|
error:
|
||||||
|
update: Eintrag konnte nicht aktualisiert werden
|
||||||
|
delete: Eintrag konnte nicht gelöscht werden
|
||||||
|
tab:
|
||||||
|
details: Details
|
||||||
|
keyValue: Extra
|
||||||
|
history: Verlauf
|
||||||
|
|
||||||
|
dialog:
|
||||||
|
delete:
|
||||||
|
title: Eintrag löschen
|
||||||
|
question: Soll der Eintrag wirklich gelöscht werden?
|
||||||
|
label: Löschen
|
||||||
|
unsavedChanges:
|
||||||
|
title: Nicht gespeicherte Änderungen
|
||||||
|
question: Sollen die Änderungen verworfen werden?
|
||||||
|
label: Verwerfen
|
||||||
|
en:
|
||||||
|
save: Save
|
||||||
|
abort: Abort
|
||||||
|
edit: Edit
|
||||||
|
noEdit: Read Mode
|
||||||
|
delete: Delete
|
||||||
|
success:
|
||||||
|
update: Entry successfully updated
|
||||||
|
delete: Entry successfully removed
|
||||||
|
error:
|
||||||
|
update: Entry could not be updated
|
||||||
|
delete: Entry could not be deleted
|
||||||
|
tab:
|
||||||
|
details: Details
|
||||||
|
keyValue: Extra
|
||||||
|
history: History
|
||||||
|
|
||||||
|
dialog:
|
||||||
|
delete:
|
||||||
|
title: Delete Entry
|
||||||
|
question: Should the entry really be deleted?
|
||||||
|
label: Delete
|
||||||
|
unsavedChanges:
|
||||||
|
title: Unsaved changes
|
||||||
|
question: Should the changes be discarded?
|
||||||
|
label: discard
|
||||||
|
</i18n>
|
||||||
|
|||||||
@ -1 +1,133 @@
|
|||||||
<template><div>create item</div></template>
|
<template>
|
||||||
|
<div>
|
||||||
|
<HaexPassItem
|
||||||
|
:default-icon="currentGroup?.icon"
|
||||||
|
:history="item.history"
|
||||||
|
@close="onClose"
|
||||||
|
v-model:details="item.details"
|
||||||
|
v-model:key-values-add="item.keyValuesAdd"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-15 sm:items-center items-end"
|
||||||
|
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-center w-full">
|
||||||
|
<UiTooltip :tooltip="t('abort')">
|
||||||
|
<UiButton
|
||||||
|
class="btn-error btn-square"
|
||||||
|
@click="onClose"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:close" />
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
</div>
|
||||||
|
<UiTooltip :tooltip="t('create')">
|
||||||
|
<UiButton
|
||||||
|
class="btn-xl btn-square btn-primary"
|
||||||
|
@click="onCreateAsync"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="mdi:content-save-outline"
|
||||||
|
class="size-11 shrink-0"
|
||||||
|
/>
|
||||||
|
</UiButton>
|
||||||
|
</UiTooltip>
|
||||||
|
<div class="flex items-center justify-center w-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type {
|
||||||
|
SelectHaexPasswordsItemDetails,
|
||||||
|
SelectHaexPasswordsItemHistory,
|
||||||
|
SelectHaexPasswordsItemKeyValues,
|
||||||
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
name: 'passwordItemCreate',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
icon: String,
|
||||||
|
title: String,
|
||||||
|
withCopyButton: Boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { isVisible } = storeToRefs(useSidebarStore())
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const item = reactive<{
|
||||||
|
details: SelectHaexPasswordsItemDetails
|
||||||
|
history: SelectHaexPasswordsItemHistory[]
|
||||||
|
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
originalDetails: string | null
|
||||||
|
originalKeyValues: string | null
|
||||||
|
}>({
|
||||||
|
details: {
|
||||||
|
id: '',
|
||||||
|
createdAt: null,
|
||||||
|
icon: null,
|
||||||
|
note: null,
|
||||||
|
password: null,
|
||||||
|
tags: null,
|
||||||
|
title: null,
|
||||||
|
updateAt: null,
|
||||||
|
url: null,
|
||||||
|
username: null,
|
||||||
|
},
|
||||||
|
history: [],
|
||||||
|
keyValuesAdd: [],
|
||||||
|
keyValuesDelete: [],
|
||||||
|
originalDetails: null,
|
||||||
|
originalKeyValues: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { add } = useSnackbar()
|
||||||
|
const { currentGroup } = storeToRefs(usePasswordGroupStore())
|
||||||
|
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||||
|
const { addAsync } = usePasswordItemStore()
|
||||||
|
|
||||||
|
const onCreateAsync = async () => {
|
||||||
|
try {
|
||||||
|
const newId = await addAsync(
|
||||||
|
item.details,
|
||||||
|
item.keyValuesAdd,
|
||||||
|
currentGroup.value,
|
||||||
|
)
|
||||||
|
if (newId) {
|
||||||
|
add({ type: 'success', text: t('success') })
|
||||||
|
syncGroupItemsAsync(currentGroup.value?.id)
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
add({ type: 'error', text: t('error') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => useRouter().back()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
de:
|
||||||
|
create: Anlegen
|
||||||
|
abort: Abbrechen
|
||||||
|
success: Eintrag erfolgreich erstellt
|
||||||
|
error: Eintrag konnte nicht erstellt werden
|
||||||
|
tab:
|
||||||
|
details: Details
|
||||||
|
keyValue: Extra
|
||||||
|
history: Verlauf
|
||||||
|
en:
|
||||||
|
create: Create
|
||||||
|
abort: Abort
|
||||||
|
success: Entry successfully created
|
||||||
|
error: Entry could not be created
|
||||||
|
tab:
|
||||||
|
details: Details
|
||||||
|
keyValue: Extra
|
||||||
|
history: History
|
||||||
|
</i18n>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'flyonui/flyonui'
|
|||||||
import { Observer } from 'tailwindcss-intersect'
|
import { Observer } from 'tailwindcss-intersect'
|
||||||
|
|
||||||
Observer.start()
|
Observer.start()
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
HSOverlay,
|
HSOverlay,
|
||||||
IStaticMethods,
|
IStaticMethods,
|
||||||
|
|||||||
BIN
src/public/horn-fragend.png
Normal file
BIN
src/public/horn-fragend.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@ -1,52 +1,78 @@
|
|||||||
import { and, eq, isNull, sql, type SQLWrapper } from 'drizzle-orm'
|
import { eq, isNull, sql } from 'drizzle-orm'
|
||||||
|
import type { IPasswordMenuItem } from '~/components/haex/pass/mobile/menu/types'
|
||||||
import {
|
import {
|
||||||
haexPasswordsGroupItems,
|
haexPasswordsGroupItems,
|
||||||
haexPasswordsGroups,
|
haexPasswordsGroups,
|
||||||
haexPasswordsItems,
|
|
||||||
type InsertHaexPasswordsGroups,
|
type InsertHaexPasswordsGroups,
|
||||||
type InsertHaexPasswordsItems,
|
type SelectHaexPasswordsGroupItems,
|
||||||
type SelectHaexPasswordsGroups,
|
type SelectHaexPasswordsGroups,
|
||||||
type SelectHaexPasswordsItems,
|
type SelectHaexPasswordsItemDetails,
|
||||||
} from '~~/src-tauri/database/schemas/vault'
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
|
export const trashId = 'trash'
|
||||||
|
|
||||||
export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
|
export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
|
||||||
const groups = ref<SelectHaexPasswordsGroups[]>([])
|
const groups = ref<SelectHaexPasswordsGroups[]>([])
|
||||||
|
|
||||||
const currentGroupId = computed<string | null>({
|
const currentGroupId = computed<string | null | undefined>({
|
||||||
get: () =>
|
get: () =>
|
||||||
getSingleRouteParam(useRouter().currentRoute.value.params.groupId) ||
|
getSingleRouteParam(useRouter().currentRoute.value.params.groupId) ||
|
||||||
null,
|
undefined,
|
||||||
set: (newGroupId) => {
|
set: (newGroupId) => {
|
||||||
console.log('set groupId', newGroupId)
|
console.log('set groupId', newGroupId)
|
||||||
useRouter().currentRoute.value.params.groupId = newGroupId ?? ''
|
useRouter().currentRoute.value.params.groupId = newGroupId ?? ''
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentGroup = ref()
|
const currentGroup = computedAsync(() =>
|
||||||
|
currentGroupId.value ? readGroupAsync(currentGroupId.value) : null,
|
||||||
|
)
|
||||||
|
|
||||||
const currentGroupItems = reactive<{
|
const currentGroupItems = reactive<{
|
||||||
items: SelectHaexPasswordsItems[]
|
items: SelectHaexPasswordsItemDetails[]
|
||||||
groups: SelectHaexPasswordsGroups[]
|
groups: SelectHaexPasswordsGroups[]
|
||||||
}>({
|
}>({
|
||||||
items: [],
|
items: [],
|
||||||
groups: [],
|
groups: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const syncGroupItemsAsync = async (currentGroupId: string | null) => {
|
const selectedGroupItems = ref<IPasswordMenuItem[]>()
|
||||||
|
|
||||||
|
const breadCrumbs = computed(() => getParentChain(currentGroupId.value))
|
||||||
|
|
||||||
|
const getParentChain = (
|
||||||
|
groupId?: string | null,
|
||||||
|
chain: SelectHaexPasswordsGroups[] = [],
|
||||||
|
) => {
|
||||||
|
const group = groups.value.find((group) => group.id === groupId)
|
||||||
|
console.log('getParentChain1: found group', group, chain)
|
||||||
|
if (group) {
|
||||||
|
chain.push(group)
|
||||||
|
console.log('getParentChain: found group', group, chain)
|
||||||
|
return getParentChain(group.parentId, chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncGroupItemsAsync = async (currentGroupId?: string | null) => {
|
||||||
const { addNotificationAsync } = useNotificationStore()
|
const { addNotificationAsync } = useNotificationStore()
|
||||||
const { readByGroupIdAsync } = usePasswordItemStore()
|
const { readByGroupIdAsync } = usePasswordItemStore()
|
||||||
/* const { currentGroup, groups, currentGroupItems } = storeToRefs(
|
|
||||||
usePasswordGroupStore(),
|
|
||||||
) */
|
|
||||||
groups.value = await readGroupsAsync()
|
groups.value = await readGroupsAsync()
|
||||||
currentGroup.value = groups.value?.find(
|
currentGroup.value = groups.value?.find(
|
||||||
(group) => group.id === currentGroupId,
|
(group) => group.id === currentGroupId,
|
||||||
)
|
)
|
||||||
|
console.log(
|
||||||
|
'syncGroupItemsAsync',
|
||||||
|
groups.value,
|
||||||
|
currentGroup.value,
|
||||||
|
currentGroupId,
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
currentGroupItems.groups =
|
currentGroupItems.groups =
|
||||||
(await getByParentIdAsync(currentGroupId)) ?? []
|
(await getByParentIdAsync(currentGroupId)) ?? []
|
||||||
currentGroupItems.items = (await readByGroupIdAsync(currentGroupId)) ?? []
|
currentGroupItems.items = (await readByGroupIdAsync(currentGroupId)) ?? []
|
||||||
console.log('search current group', groups.value, currentGroup.value)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
currentGroupItems.groups = []
|
currentGroupItems.groups = []
|
||||||
@ -57,34 +83,45 @@ export const usePasswordGroupStore = defineStore('passwordGroupStore', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
watch(
|
|
||||||
currentGroupId,
|
watch(currentGroupId, () => syncGroupItemsAsync(currentGroupId.value), {
|
||||||
async () => {
|
immediate: true,
|
||||||
syncGroupItemsAsync(currentGroupId.value)
|
})
|
||||||
},
|
|
||||||
{ immediate: true },
|
const inTrashGroup = computed(() =>
|
||||||
|
breadCrumbs.value?.some((item) => item.id === trashId),
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addGroupAsync,
|
addGroupAsync,
|
||||||
|
breadCrumbs,
|
||||||
|
createTrashIfNotExistsAsync,
|
||||||
currentGroup,
|
currentGroup,
|
||||||
currentGroupId,
|
currentGroupId,
|
||||||
currentGroupItems,
|
currentGroupItems,
|
||||||
|
deleteGroupAsync,
|
||||||
|
getChildGroupsRecursiveAsync,
|
||||||
groups,
|
groups,
|
||||||
|
inTrashGroup,
|
||||||
|
insertGroupItemsAsync,
|
||||||
navigateToGroupAsync,
|
navigateToGroupAsync,
|
||||||
navigateToGroupItemsAsync,
|
navigateToGroupItemsAsync,
|
||||||
readGroupAsync,
|
readGroupAsync,
|
||||||
readGroupItemsAsync,
|
readGroupItemsAsync,
|
||||||
readGroupsAsync,
|
readGroupsAsync,
|
||||||
|
selectedGroupItems,
|
||||||
|
syncGroupItemsAsync,
|
||||||
|
trashId,
|
||||||
updateAsync,
|
updateAsync,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
|
const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
|
||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
|
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||||
|
|
||||||
const newGroup: InsertHaexPasswordsGroups = {
|
const newGroup: InsertHaexPasswordsGroups = {
|
||||||
id: crypto.randomUUID(),
|
id: group.id || crypto.randomUUID(),
|
||||||
parentId: group.parentId,
|
parentId: group.parentId,
|
||||||
color: group.color,
|
color: group.color,
|
||||||
icon: group.icon,
|
icon: group.icon,
|
||||||
@ -92,6 +129,7 @@ const addGroupAsync = async (group: Partial<InsertHaexPasswordsGroups>) => {
|
|||||||
order: group.order,
|
order: group.order,
|
||||||
}
|
}
|
||||||
await currentVault.drizzle.insert(haexPasswordsGroups).values(newGroup)
|
await currentVault.drizzle.insert(haexPasswordsGroups).values(newGroup)
|
||||||
|
await syncGroupItemsAsync()
|
||||||
return newGroup
|
return newGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,21 +155,47 @@ const readGroupsAsync = async (filter?: { parentId?: string | null }) => {
|
|||||||
return await currentVault.value.drizzle
|
return await currentVault.value.drizzle
|
||||||
.select()
|
.select()
|
||||||
.from(haexPasswordsGroups)
|
.from(haexPasswordsGroups)
|
||||||
.where(isNull(haexPasswordsGroups.parentId))
|
|
||||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const readGroupItemsAsync = async (id?: string | null) => {
|
const readGroupItemsAsync = async (
|
||||||
|
groupId?: string | null,
|
||||||
|
): Promise<SelectHaexPasswordsGroupItems[]> => {
|
||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
|
|
||||||
currentVault.drizzle.select().from(haexPasswordsGroupItems)
|
if (groupId) {
|
||||||
|
return currentVault.drizzle
|
||||||
|
.select()
|
||||||
|
.from(haexPasswordsGroupItems)
|
||||||
|
.where(eq(haexPasswordsGroupItems.groupId, groupId))
|
||||||
|
} else {
|
||||||
|
return currentVault.drizzle
|
||||||
|
.select()
|
||||||
|
.from(haexPasswordsGroupItems)
|
||||||
|
.where(isNull(haexPasswordsGroupItems.groupId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getByParentIdAsync = async (parentId?: string | null) => {
|
const getChildGroupsRecursiveAsync = async (
|
||||||
|
groupId: string,
|
||||||
|
groups: SelectHaexPasswordsGroups[] = [],
|
||||||
|
) => {
|
||||||
|
const childGroups = (await getByParentIdAsync(groupId)) ?? []
|
||||||
|
for (const child of childGroups) {
|
||||||
|
groups.push(...(await getChildGroupsRecursiveAsync(child.id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
const getByParentIdAsync = async (
|
||||||
|
parentId?: string | null,
|
||||||
|
): Promise<SelectHaexPasswordsGroups[]> => {
|
||||||
try {
|
try {
|
||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
|
|
||||||
|
console.log('getByParentIdAsync', parentId)
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
const groups = await currentVault.drizzle
|
const groups = await currentVault.drizzle
|
||||||
.select()
|
.select()
|
||||||
@ -139,7 +203,6 @@ const getByParentIdAsync = async (parentId?: string | null) => {
|
|||||||
.where(eq(haexPasswordsGroups.parentId, parentId))
|
.where(eq(haexPasswordsGroups.parentId, parentId))
|
||||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||||
|
|
||||||
console.log('found groups', groups)
|
|
||||||
return groups
|
return groups
|
||||||
} else {
|
} else {
|
||||||
const groups = await currentVault.drizzle
|
const groups = await currentVault.drizzle
|
||||||
@ -148,11 +211,11 @@ const getByParentIdAsync = async (parentId?: string | null) => {
|
|||||||
.where(isNull(haexPasswordsGroups.parentId))
|
.where(isNull(haexPasswordsGroups.parentId))
|
||||||
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
.orderBy(sql`${haexPasswordsGroups.order} nulls last`)
|
||||||
|
|
||||||
console.log('found groups', groups)
|
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,14 +233,22 @@ const navigateToGroupAsync = (groupId?: string | null) =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateAsync = async () => {}
|
const updateAsync = async (group: InsertHaexPasswordsGroups) => {
|
||||||
|
console.log('updateAsync', group)
|
||||||
|
const { currentVault } = storeToRefs(useVaultStore())
|
||||||
|
if (!group.id) return
|
||||||
|
|
||||||
|
return currentVault.value.drizzle
|
||||||
|
.update(haexPasswordsGroups)
|
||||||
|
.set(group)
|
||||||
|
.where(eq(haexPasswordsGroups.id, group.id))
|
||||||
|
}
|
||||||
|
|
||||||
const navigateToGroupItemsAsync = (groupId: string) => {
|
const navigateToGroupItemsAsync = (groupId: string) => {
|
||||||
navigateTo(
|
return navigateTo(
|
||||||
useLocaleRoute()({
|
useLocaleRoute()({
|
||||||
name: 'passwordGroupItems',
|
name: 'passwordGroupItems',
|
||||||
params: {
|
params: {
|
||||||
vaultId: useRouter().currentRoute.value.params.vaultId,
|
|
||||||
groupId,
|
groupId,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
@ -186,3 +257,80 @@ const navigateToGroupItemsAsync = (groupId: string) => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const insertGroupItemsAsync = async (
|
||||||
|
items: IPasswordMenuItem[],
|
||||||
|
groupdId?: string | null,
|
||||||
|
) => {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
const { groups } = usePasswordGroupStore()
|
||||||
|
const { syncGroupItemsAsync } = usePasswordGroupStore()
|
||||||
|
|
||||||
|
const targetGroup = groups.find((group) => group.id === groupdId)
|
||||||
|
|
||||||
|
console.log('insertGroupItemsAsync', items, targetGroup)
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type === 'group') {
|
||||||
|
const updateGroup = groups.find((group) => group.id === item.id)
|
||||||
|
|
||||||
|
if (updateGroup?.parentId === targetGroup?.id) return
|
||||||
|
|
||||||
|
if (updateGroup) {
|
||||||
|
updateGroup.parentId = targetGroup?.id ?? null
|
||||||
|
await currentVault.drizzle
|
||||||
|
.update(haexPasswordsGroups)
|
||||||
|
.set(updateGroup)
|
||||||
|
.where(eq(haexPasswordsGroups.id, updateGroup.id))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (targetGroup)
|
||||||
|
await currentVault.drizzle
|
||||||
|
.update(haexPasswordsGroupItems)
|
||||||
|
.set({ groupId: targetGroup.id, itemId: item.id })
|
||||||
|
.where(eq(haexPasswordsGroupItems.itemId, item.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syncGroupItemsAsync(targetGroup?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTrashIfNotExistsAsync = async () => {
|
||||||
|
const exists = await readGroupAsync(trashId)
|
||||||
|
console.log('found trash', exists)
|
||||||
|
if (exists) return true
|
||||||
|
|
||||||
|
return addGroupAsync({
|
||||||
|
name: 'Trash',
|
||||||
|
id: trashId,
|
||||||
|
icon: 'mdi:trash-outline',
|
||||||
|
parentId: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteGroupAsync = async (groupId: string, final: boolean = false) => {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
const { readByGroupIdAsync, deleteAsync } = usePasswordItemStore()
|
||||||
|
|
||||||
|
console.log('deleteGroupAsync', groupId, final)
|
||||||
|
|
||||||
|
if (final || groupId === trashId) {
|
||||||
|
const childGroups = await getByParentIdAsync(groupId)
|
||||||
|
|
||||||
|
for (const child of childGroups) {
|
||||||
|
await deleteGroupAsync(child.id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = await readByGroupIdAsync(groupId)
|
||||||
|
console.log('deleteGroupAsync delete Items', items)
|
||||||
|
for (const item of items) {
|
||||||
|
await deleteAsync(item.id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await currentVault.drizzle
|
||||||
|
.delete(haexPasswordsGroups)
|
||||||
|
.where(eq(haexPasswordsGroups.id, groupId))
|
||||||
|
} else {
|
||||||
|
if (await createTrashIfNotExistsAsync())
|
||||||
|
await updateAsync({ id: groupId, parentId: trashId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import { eq, isNull } from 'drizzle-orm'
|
import { eq, isNull } from 'drizzle-orm'
|
||||||
import {
|
import {
|
||||||
haexPasswordsGroupItems,
|
haexPasswordsGroupItems,
|
||||||
haexPasswordsGroups,
|
haexPasswordsItemDetails,
|
||||||
haexPasswordsItems,
|
haexPasswordsItemHistory,
|
||||||
type InsertHaexPasswordsItems,
|
haexPasswordsItemKeyValues,
|
||||||
type InsertHaexPasswordsItemsKeyValues,
|
type InsertHaexPasswordsItemDetails,
|
||||||
|
type InserthaexPasswordsItemKeyValues,
|
||||||
|
type SelectHaexPasswordsGroups,
|
||||||
|
type SelectHaexPasswordsItemDetails,
|
||||||
|
type SelectHaexPasswordsItemKeyValues,
|
||||||
} from '~~/src-tauri/database/schemas/vault'
|
} from '~~/src-tauri/database/schemas/vault'
|
||||||
|
|
||||||
export const usePasswordItemStore = defineStore('passwordItemStore', () => {
|
export const usePasswordItemStore = defineStore('passwordItemStore', () => {
|
||||||
@ -17,34 +21,114 @@ export const usePasswordItemStore = defineStore('passwordItemStore', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const currentItem = computedAsync(
|
||||||
|
async () => await readAsync(currentItemId.value),
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentItemId,
|
currentItemId,
|
||||||
|
currentItem,
|
||||||
addAsync,
|
addAsync,
|
||||||
|
addKeyValueAsync,
|
||||||
|
addKeyValuesAsync,
|
||||||
|
deleteAsync,
|
||||||
|
deleteKeyValueAsync,
|
||||||
readByGroupIdAsync,
|
readByGroupIdAsync,
|
||||||
readAsync,
|
readAsync,
|
||||||
readKeyValuesAsync,
|
readKeyValuesAsync,
|
||||||
|
updateAsync,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const addAsync = async (
|
const addAsync = async (
|
||||||
item: InsertHaexPasswordsItems,
|
details: SelectHaexPasswordsItemDetails,
|
||||||
keyValues: InsertHaexPasswordsItemsKeyValues,
|
keyValues: SelectHaexPasswordsItemKeyValues[],
|
||||||
|
group?: SelectHaexPasswordsGroups | null,
|
||||||
) => {
|
) => {
|
||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
/* const { currentGroupId } = useVaultGroupStore();
|
console.log('addItem', details, group)
|
||||||
|
|
||||||
entry.id = crypto.randomUUID();
|
const newDetails: InsertHaexPasswordsItemDetails = {
|
||||||
entry.createdAt = null;
|
id: crypto.randomUUID(),
|
||||||
entry.updateAt = null;
|
icon: details.icon || group?.icon || null,
|
||||||
console.log('store create entry', entry, currentGroupId);
|
note: details.note,
|
||||||
await currentVault?.drizzle.transaction(async (tx) => {
|
password: details.password,
|
||||||
await tx.insert(vaultEntry).values(entry);
|
tags: details.tags,
|
||||||
await tx
|
title: details.title,
|
||||||
.insert(vaultGroupEntry)
|
url: details.url,
|
||||||
.values({ entryId: entry.id, groupId: currentGroupId });
|
username: details.username,
|
||||||
});
|
}
|
||||||
|
|
||||||
return entry.id; */
|
const newKeyValues: InserthaexPasswordsItemKeyValues[] = keyValues.map(
|
||||||
|
(keyValue) => ({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
itemId: newDetails.id,
|
||||||
|
key: keyValue.key,
|
||||||
|
value: keyValue.value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await currentVault?.drizzle.transaction(async (tx) => {
|
||||||
|
await tx.insert(haexPasswordsItemDetails).values(newDetails)
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.insert(haexPasswordsGroupItems)
|
||||||
|
.values({ itemId: newDetails.id, groupId: group?.id ?? null })
|
||||||
|
|
||||||
|
if (newKeyValues.length)
|
||||||
|
await tx.insert(haexPasswordsItemKeyValues).values(newKeyValues)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ERROR addItem', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDetails.id
|
||||||
|
}
|
||||||
|
|
||||||
|
const addKeyValueAsync = async (
|
||||||
|
item?: InserthaexPasswordsItemKeyValues | null,
|
||||||
|
itemId?: string,
|
||||||
|
) => {
|
||||||
|
const newKeyValue: InserthaexPasswordsItemKeyValues = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
itemId: item?.itemId || itemId,
|
||||||
|
key: item?.key,
|
||||||
|
value: item?.value,
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
return await currentVault?.drizzle
|
||||||
|
.insert(haexPasswordsItemKeyValues)
|
||||||
|
.values(newKeyValue)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ERROR addItem', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addKeyValuesAsync = async (
|
||||||
|
items: InserthaexPasswordsItemKeyValues[],
|
||||||
|
itemId?: string,
|
||||||
|
) => {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
console.log('addKeyValues', items, itemId)
|
||||||
|
|
||||||
|
const newKeyValues: InserthaexPasswordsItemKeyValues[] = items?.map(
|
||||||
|
(item) => ({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
itemId: item.itemId || itemId,
|
||||||
|
key: item.key,
|
||||||
|
value: item.value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await currentVault?.drizzle
|
||||||
|
.insert(haexPasswordsItemKeyValues)
|
||||||
|
.values(newKeyValues)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ERROR addItem', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const readByGroupIdAsync = async (groupId?: string | null) => {
|
const readByGroupIdAsync = async (groupId?: string | null) => {
|
||||||
@ -58,25 +142,25 @@ const readByGroupIdAsync = async (groupId?: string | null) => {
|
|||||||
.select()
|
.select()
|
||||||
.from(haexPasswordsGroupItems)
|
.from(haexPasswordsGroupItems)
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
haexPasswordsItems,
|
haexPasswordsItemDetails,
|
||||||
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
|
eq(haexPasswordsItemDetails.id, haexPasswordsGroupItems.itemId),
|
||||||
)
|
)
|
||||||
.where(eq(haexPasswordsGroupItems.groupId, groupId))
|
.where(eq(haexPasswordsGroupItems.groupId, groupId))
|
||||||
|
|
||||||
console.log('found entries by groupId', entries)
|
console.log('found entries by groupId', entries)
|
||||||
return entries.map((entry) => entry.haex_passwords_items)
|
return entries.map((entry) => entry.haex_passwords_item_details)
|
||||||
} else {
|
} else {
|
||||||
const entries = await currentVault.drizzle
|
const entries = await currentVault.drizzle
|
||||||
.select()
|
.select()
|
||||||
.from(haexPasswordsGroupItems)
|
.from(haexPasswordsGroupItems)
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
haexPasswordsItems,
|
haexPasswordsItemDetails,
|
||||||
eq(haexPasswordsItems.id, haexPasswordsGroupItems.itemId),
|
eq(haexPasswordsItemDetails.id, haexPasswordsGroupItems.itemId),
|
||||||
)
|
)
|
||||||
.where(isNull(haexPasswordsGroupItems.groupId))
|
.where(isNull(haexPasswordsGroupItems.groupId))
|
||||||
|
|
||||||
console.log('found entries', entries)
|
console.log('found entries', entries)
|
||||||
return entries.map((entry) => entry.haex_passwords_items)
|
return entries.map((entry) => entry.haex_passwords_item_details)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@ -91,11 +175,11 @@ const readAsync = async (itemId: string | null) => {
|
|||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
|
|
||||||
const details =
|
const details =
|
||||||
await currentVault.drizzle.query.haexPasswordsItems.findFirst({
|
await currentVault.drizzle.query.haexPasswordsItemDetails.findFirst({
|
||||||
where: eq(haexPasswordsItems.id, itemId),
|
where: eq(haexPasswordsItemDetails.id, itemId),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!details) return {}
|
if (!details) return null
|
||||||
|
|
||||||
const history = (await usePasswordHistoryStore().getAsync(itemId)) ?? []
|
const history = (await usePasswordHistoryStore().getAsync(itemId)) ?? []
|
||||||
const keyValues = (await readKeyValuesAsync(itemId)) ?? []
|
const keyValues = (await readKeyValuesAsync(itemId)) ?? []
|
||||||
@ -113,8 +197,127 @@ const readKeyValuesAsync = async (itemId: string | null) => {
|
|||||||
const { currentVault } = useVaultStore()
|
const { currentVault } = useVaultStore()
|
||||||
|
|
||||||
const keyValues =
|
const keyValues =
|
||||||
await currentVault.drizzle.query.haexPasswordsItemsKeyValues.findMany({
|
await currentVault.drizzle.query.haexPasswordsItemKeyValues.findMany({
|
||||||
where: eq(haexPasswordsItems.id, itemId),
|
where: eq(haexPasswordsGroupItems.itemId, itemId),
|
||||||
})
|
})
|
||||||
return keyValues
|
return keyValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateAsync = async ({
|
||||||
|
details,
|
||||||
|
keyValues,
|
||||||
|
keyValuesAdd,
|
||||||
|
keyValuesDelete,
|
||||||
|
groupId,
|
||||||
|
}: {
|
||||||
|
details: SelectHaexPasswordsItemDetails
|
||||||
|
keyValues: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
keyValuesAdd: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
keyValuesDelete: SelectHaexPasswordsItemKeyValues[]
|
||||||
|
groupId: string | null
|
||||||
|
}) => {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
|
||||||
|
if (!details.id) return
|
||||||
|
|
||||||
|
const newDetails: InsertHaexPasswordsItemDetails = {
|
||||||
|
id: details.id,
|
||||||
|
icon: details.icon,
|
||||||
|
note: details.note,
|
||||||
|
password: details.password,
|
||||||
|
tags: details.tags,
|
||||||
|
title: details.title,
|
||||||
|
url: details.url,
|
||||||
|
username: details.username,
|
||||||
|
}
|
||||||
|
|
||||||
|
const newKeyValues: InserthaexPasswordsItemKeyValues[] = keyValues
|
||||||
|
.map((keyValue) => ({
|
||||||
|
id: keyValue.id,
|
||||||
|
itemId: newDetails.id,
|
||||||
|
key: keyValue.key,
|
||||||
|
value: keyValue.value,
|
||||||
|
}))
|
||||||
|
.filter((keyValue) => keyValue.id)
|
||||||
|
|
||||||
|
const newKeyValuesAdd: InserthaexPasswordsItemKeyValues[] = keyValuesAdd.map(
|
||||||
|
(keyValue) => ({
|
||||||
|
id: keyValue.id || crypto.randomUUID(),
|
||||||
|
itemId: newDetails.id,
|
||||||
|
key: keyValue.key,
|
||||||
|
value: keyValue.value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('update item', newDetails, newKeyValues, newKeyValuesAdd, groupId)
|
||||||
|
|
||||||
|
return await currentVault?.drizzle.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
|
.update(haexPasswordsItemDetails)
|
||||||
|
.set(newDetails)
|
||||||
|
.where(eq(haexPasswordsItemDetails.id, newDetails.id))
|
||||||
|
|
||||||
|
await tx
|
||||||
|
.update(haexPasswordsGroupItems)
|
||||||
|
.set({ itemId: newDetails.id, groupId })
|
||||||
|
.where(eq(haexPasswordsGroupItems.itemId, newDetails.id))
|
||||||
|
|
||||||
|
const promises = newKeyValues.map((keyValue) =>
|
||||||
|
tx
|
||||||
|
.update(haexPasswordsItemKeyValues)
|
||||||
|
.set(keyValue)
|
||||||
|
.where(eq(haexPasswordsItemKeyValues.id, keyValue.id)),
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
if (newKeyValuesAdd.length)
|
||||||
|
await tx.insert(haexPasswordsItemKeyValues).values(newKeyValuesAdd)
|
||||||
|
|
||||||
|
const promisesDelete = keyValuesDelete.map((keyValue) =>
|
||||||
|
tx
|
||||||
|
.delete(haexPasswordsItemKeyValues)
|
||||||
|
.where(eq(haexPasswordsItemKeyValues.id, keyValue.id)),
|
||||||
|
)
|
||||||
|
await Promise.all(promisesDelete)
|
||||||
|
|
||||||
|
return newDetails.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAsync = async (itemId: string, final: boolean = false) => {
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
const { createTrashIfNotExistsAsync, trashId } = usePasswordGroupStore()
|
||||||
|
|
||||||
|
console.log('deleteAsync', itemId, final)
|
||||||
|
if (final)
|
||||||
|
await currentVault?.drizzle.transaction(async (tx) => {
|
||||||
|
await tx
|
||||||
|
.delete(haexPasswordsItemKeyValues)
|
||||||
|
.where(eq(haexPasswordsItemKeyValues.itemId, itemId))
|
||||||
|
await tx
|
||||||
|
.delete(haexPasswordsItemHistory)
|
||||||
|
.where(eq(haexPasswordsItemHistory.itemId, itemId))
|
||||||
|
await tx
|
||||||
|
.delete(haexPasswordsGroupItems)
|
||||||
|
.where(eq(haexPasswordsGroupItems.itemId, itemId))
|
||||||
|
await tx
|
||||||
|
.delete(haexPasswordsItemDetails)
|
||||||
|
.where(eq(haexPasswordsItemDetails.id, itemId))
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
if (await createTrashIfNotExistsAsync())
|
||||||
|
await currentVault.drizzle
|
||||||
|
.update(haexPasswordsGroupItems)
|
||||||
|
.set({ groupId: trashId })
|
||||||
|
.where(eq(haexPasswordsGroupItems.itemId, itemId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteKeyValueAsync = async (id: string) => {
|
||||||
|
console.log('deleteKeyValueAsync', id)
|
||||||
|
const { currentVault } = useVaultStore()
|
||||||
|
return await currentVault.drizzle
|
||||||
|
.delete(haexPasswordsItemKeyValues)
|
||||||
|
.where(eq(haexPasswordsItemKeyValues.id, id))
|
||||||
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export const useUiStore = defineStore('uiStore', () => {
|
|||||||
const breakpoints = useBreakpoints(breakpointsTailwind)
|
const breakpoints = useBreakpoints(breakpointsTailwind)
|
||||||
|
|
||||||
const currentScreenSize = computed(() =>
|
const currentScreenSize = computed(() =>
|
||||||
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs'
|
breakpoints.active().value.length > 0 ? breakpoints.active().value : 'xs',
|
||||||
)
|
)
|
||||||
|
|
||||||
const { t } = useI18n({
|
const { t } = useI18n({
|
||||||
@ -45,11 +45,14 @@ export const useUiStore = defineStore('uiStore', () => {
|
|||||||
|
|
||||||
const currentTheme = ref(defaultTheme)
|
const currentTheme = ref(defaultTheme)
|
||||||
|
|
||||||
|
const currentThemeValue = computed(() => currentTheme.value.value)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availableThemes,
|
availableThemes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
currentScreenSize,
|
currentScreenSize,
|
||||||
currentTheme,
|
currentTheme,
|
||||||
|
currentThemeValue,
|
||||||
defaultTheme,
|
defaultTheme,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -27,20 +27,8 @@ export const useSidebarStore = defineStore('sidebarStore', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
/* const loadAsync = async (id: string) => {
|
|
||||||
extensions.value.some(async (extension) => {
|
|
||||||
if (extension.id === id) {
|
|
||||||
await navigateTo(
|
|
||||||
useLocalePath()({ name: 'extension', params: { extensionId: id } })
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}; */
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menu,
|
menu,
|
||||||
isVisible,
|
isVisible,
|
||||||
//loadAsync,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import * as schema from '@/../src-tauri/database/schemas/vault'
|
import * as schema from '@/../src-tauri/database/schemas/vault'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import { exists } from '@tauri-apps/plugin-fs'
|
||||||
import { platform } from '@tauri-apps/plugin-os'
|
import { platform } from '@tauri-apps/plugin-os'
|
||||||
import { eq } from 'drizzle-orm'
|
import { eq } from 'drizzle-orm'
|
||||||
import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'
|
import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'
|
||||||
@ -120,8 +121,7 @@ export const useVaultStore = defineStore('vaultStore', () => {
|
|||||||
return vaultId
|
return vaultId
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error openAsync ', error)
|
console.error('Error openAsync ', error)
|
||||||
throw new Error(JSON.stringify(error))
|
throw error
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { LocationQueryValue, RouteLocationRawI18n } from 'vue-router'
|
|||||||
|
|
||||||
export const bytesToBase64DataUrlAsync = async (
|
export const bytesToBase64DataUrlAsync = async (
|
||||||
bytes: Uint8Array,
|
bytes: Uint8Array,
|
||||||
type = 'application/octet-stream'
|
type = 'application/octet-stream',
|
||||||
) => {
|
) => {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
const reader = Object.assign(new FileReader(), {
|
const reader = Object.assign(new FileReader(), {
|
||||||
@ -65,16 +65,16 @@ export const readableFileSize = (sizeInByte: number | string = 0) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getSingleRouteParam = (
|
export const getSingleRouteParam = (
|
||||||
param: string | string[] | LocationQueryValue | LocationQueryValue[]
|
param: string | string[] | LocationQueryValue | LocationQueryValue[],
|
||||||
): string => {
|
): string => {
|
||||||
const _param = Array.isArray(param) ? param.at(0) ?? '' : param ?? ''
|
const _param = Array.isArray(param) ? param.at(0) ?? '' : param ?? ''
|
||||||
//console.log('found param', _param, param);
|
console.log('getSingleRouteParam found:', _param, param)
|
||||||
return decodeURIComponent(_param)
|
return decodeURIComponent(_param)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isRouteActive = (
|
export const isRouteActive = (
|
||||||
to: RouteLocationRawI18n,
|
to: RouteLocationRawI18n,
|
||||||
exact: boolean = false
|
exact: boolean = false,
|
||||||
) =>
|
) =>
|
||||||
computed(() => {
|
computed(() => {
|
||||||
const found = useRouter()
|
const found = useRouter()
|
||||||
@ -85,7 +85,7 @@ export const isRouteActive = (
|
|||||||
? found?.name === useRouter().currentRoute.value.name
|
? found?.name === useRouter().currentRoute.value.name
|
||||||
: found?.name === useRouter().currentRoute.value.name ||
|
: found?.name === useRouter().currentRoute.value.name ||
|
||||||
found?.children.some(
|
found?.children.some(
|
||||||
(child) => child.name === useRouter().currentRoute.value.name
|
(child) => child.name === useRouter().currentRoute.value.name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ export const isKey = <T extends object>(x: T, k: PropertyKey): k is keyof T => {
|
|||||||
|
|
||||||
export const filterAsync = async <T>(
|
export const filterAsync = async <T>(
|
||||||
arr: T[],
|
arr: T[],
|
||||||
predicate: (value: T, index: number, array: T[]) => Promise<boolean>
|
predicate: (value: T, index: number, array: T[]) => Promise<boolean>,
|
||||||
) => {
|
) => {
|
||||||
// 1. Mappe jedes Element auf ein Promise, das zu true/false auflöst
|
// 1. Mappe jedes Element auf ein Promise, das zu true/false auflöst
|
||||||
const results = await Promise.all(arr.map(predicate))
|
const results = await Promise.all(arr.map(predicate))
|
||||||
@ -119,3 +119,39 @@ export const hexToString = (hex: string) => {
|
|||||||
|
|
||||||
return parsedValue ? parsedValue : ''
|
return parsedValue ? parsedValue : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getContrastingTextColor = (
|
||||||
|
hexColor: string,
|
||||||
|
): 'black' | 'white' => {
|
||||||
|
if (!hexColor) {
|
||||||
|
return 'black' // Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entferne das '#' vom Anfang
|
||||||
|
let color = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor
|
||||||
|
|
||||||
|
// Handle Kurzform-Hex-Werte (z.B. "F0C" -> "FF00CC")
|
||||||
|
if (color.length === 3) {
|
||||||
|
color = color
|
||||||
|
.split('')
|
||||||
|
.map((char) => char + char)
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color.length !== 6) {
|
||||||
|
return 'black' // Fallback für ungültige Farben
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konvertiere Hex zu RGB
|
||||||
|
const r = parseInt(color.substring(0, 2), 16)
|
||||||
|
const g = parseInt(color.substring(2, 4), 16)
|
||||||
|
const b = parseInt(color.substring(4, 6), 16)
|
||||||
|
|
||||||
|
// Berechne die wahrgenommene Luminanz nach der WCAG-Formel.
|
||||||
|
// Werte von 0 (schwarz) bis 255 (weiß).
|
||||||
|
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
||||||
|
|
||||||
|
// Wähle die Textfarbe basierend auf einem Schwellenwert.
|
||||||
|
// Ein Wert > 186 wird oft als "hell" genug für schwarzen Text angesehen.
|
||||||
|
return luminance > 186 ? 'black' : 'white'
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user