diff options
Diffstat (limited to 'front-end')
-rw-r--r-- | front-end/package-lock.json | 248 | ||||
-rw-r--r-- | front-end/package.json | 4 | ||||
-rw-r--r-- | front-end/src/App.vue | 4 | ||||
-rw-r--r-- | front-end/src/components/Bookmarks.vue | 77 | ||||
-rw-r--r-- | front-end/src/components/Boomarks/AddOrUpdateForm.vue | 53 | ||||
-rw-r--r-- | front-end/src/components/Boomarks/BookmarkList.vue | 157 | ||||
-rw-r--r-- | front-end/src/components/Login/AdminReg.vue | 16 | ||||
-rw-r--r-- | front-end/src/components/Login/PkiLogin.vue | 39 | ||||
-rw-r--r-- | front-end/src/components/Login/UserPass.vue | 13 | ||||
-rw-r--r-- | front-end/src/components/global/Dialog.vue | 2 | ||||
-rw-r--r-- | front-end/src/index.scss | 2 | ||||
-rw-r--r-- | front-end/src/main.ts | 2 | ||||
-rw-r--r-- | front-end/src/store/socialMfaPlugin.ts | 1 |
13 files changed, 380 insertions, 238 deletions
diff --git a/front-end/package-lock.json b/front-end/package-lock.json index ebc2f59..64ffa7b 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -10,7 +10,7 @@ "license": "agpl3", "dependencies": { "@headlessui/vue": "^1.7.17", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/27b487b6d0befdb2197a58ceadb1f1ac2b337786/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/a7cf7c8987b8847984629293d8eb27908f3de3dd/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.2", "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", @@ -37,7 +37,7 @@ "typescript": "^5.0.2", "vite": "^5.0.x", "vue-eslint-parser": "^9.3.0", - "vue-tsc": "^1.4.2" + "vue-tsc": "^2.0.x" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -62,9 +62,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -538,9 +538,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "peer": true }, "node_modules/@isaacs/cliui": { @@ -686,9 +686,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", "cpu": [ "arm" ], @@ -699,9 +699,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", "cpu": [ "arm64" ], @@ -712,9 +712,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", "cpu": [ "arm64" ], @@ -725,9 +725,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", "cpu": [ "x64" ], @@ -738,9 +738,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", "cpu": [ "arm" ], @@ -751,9 +751,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", "cpu": [ "arm64" ], @@ -764,9 +764,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", "cpu": [ "arm64" ], @@ -777,9 +777,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", "cpu": [ "ppc64le" ], @@ -790,9 +790,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", "cpu": [ "riscv64" ], @@ -803,9 +803,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", "cpu": [ "s390x" ], @@ -816,9 +816,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", "cpu": [ "x64" ], @@ -829,9 +829,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", "cpu": [ "x64" ], @@ -842,9 +842,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", "cpu": [ "arm64" ], @@ -855,9 +855,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", "cpu": [ "ia32" ], @@ -868,9 +868,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", "cpu": [ "x64" ], @@ -956,8 +956,8 @@ }, "node_modules/@vnuge/vnlib.browser": { "version": "0.1.13", - "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/27b487b6d0befdb2197a58ceadb1f1ac2b337786/@vnuge-vnlib.browser/release.tgz", - "integrity": "sha512-4vhBVC9vro4wwtnsNCCno0LLY5ipS2JLe4em2Ucn+BzSCzxTb75JoTWzeMLjWHxb5io6fq9LV676WU6t5nnF0Q==", + "resolved": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/a7cf7c8987b8847984629293d8eb27908f3de3dd/@vnuge-vnlib.browser/release.tgz", + "integrity": "sha512-o8jj4LlMUFc9Z082+7toRjFZB9HhGTuRlzvzSZfutAtmuWbljjEiGdl53s5MUVzshwEFjo+Kj+ehHJnPK7niIw==", "license": "MIT", "peerDependencies": { "@vueuse/core": "^10.x", @@ -970,30 +970,30 @@ } }, "node_modules/@volar/language-core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", - "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "version": "2.2.0-alpha.6", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.2.0-alpha.6.tgz", + "integrity": "sha512-GmT28LX2w4x82uuQqNN/P94VOCsZRHBbGcGe+5bFtA2hbIbH6f8tFdMfgXFtyhbft/pj6f3xl37xe+t+nomLIA==", "dev": true, "dependencies": { - "@volar/source-map": "1.11.1" + "@volar/source-map": "2.2.0-alpha.6" } }, "node_modules/@volar/source-map": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", - "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "version": "2.2.0-alpha.6", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.2.0-alpha.6.tgz", + "integrity": "sha512-EztD2zoUopETY+ZCUZAGUHKgj4gOkY/2WnaOS+RSTc56xm85miSA4qOBS8Lt1Ruu5vV52WIZKHW/R9PbjkZWFA==", "dev": true, "dependencies": { - "muggle-string": "^0.3.1" + "muggle-string": "^0.4.0" } }, "node_modules/@volar/typescript": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", - "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "version": "2.2.0-alpha.6", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.2.0-alpha.6.tgz", + "integrity": "sha512-wTr0jO3wVXQ9FjBbWE2iX8GgDoiHp1Nttsb+tKk5IeUUb6f1uOjyeIXuS4KfeMBpCufthRO2st2O2uatAs/UXQ==", "dev": true, "dependencies": { - "@volar/language-core": "1.11.1", + "@volar/language-core": "2.2.0-alpha.6", "path-browserify": "^1.0.1" } }, @@ -1049,18 +1049,16 @@ "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" }, "node_modules/@vue/language-core": { - "version": "1.8.27", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", - "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.11.tgz", + "integrity": "sha512-5ivg8Vem/yckzXI3L3n0mdKBPRcHSlsGt6/dpbEx42PcH3MIHAjSAJBYvENXeWJxv2ClQc8BS2mH1Ho2U7jZig==", "dev": true, "dependencies": { - "@volar/language-core": "~1.11.1", - "@volar/source-map": "~1.11.1", - "@vue/compiler-dom": "^3.3.0", - "@vue/shared": "^3.3.0", + "@volar/language-core": "~2.2.0-alpha.6", + "@vue/compiler-dom": "^3.4.0", + "@vue/shared": "^3.4.0", "computeds": "^0.0.1", "minimatch": "^9.0.3", - "muggle-string": "^0.3.1", "path-browserify": "^1.0.1", "vue-template-compiler": "^2.7.14" }, @@ -1549,9 +1547,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001603", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001603.tgz", - "integrity": "sha512-iL2iSS0eDILMb9n5yKQoTBim9jMZ0Yrk8g0N9K7UzYyWnfIKzXBZD5ngpM37ZcL/cv0Mli8XtVMRYMQAfFpi5Q==", + "version": "1.0.30001607", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz", + "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==", "dev": true, "funding": [ { @@ -1785,9 +1783,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.722", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz", - "integrity": "sha512-5nLE0TWFFpZ80Crhtp4pIp8LXCztjYX41yUcV6b+bKR2PqzjskTMOOlBi1VjBHlvHwS+4gar7kNKOrsbsewEZQ==", + "version": "1.4.730", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.730.tgz", + "integrity": "sha512-oJRPo82XEqtQAobHpJIR3zW5YO3sSRRkPz2an4yxi1UvqhsGm54vR/wzTFV74a3soDOJ8CKW7ajOOX5ESzddwg==", "dev": true }, "node_modules/emoji-regex": { @@ -2461,9 +2459,9 @@ } }, "node_modules/jose": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", - "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz", + "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -2588,9 +2586,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -2674,9 +2672,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/muggle-string": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", - "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", "dev": true }, "node_modules/mz": { @@ -3252,9 +3250,9 @@ } }, "node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3267,21 +3265,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", "fsevents": "~2.3.2" } }, @@ -3308,9 +3306,9 @@ } }, "node_modules/sass": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", - "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "version": "1.74.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz", + "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -3686,9 +3684,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -3699,9 +3697,9 @@ } }, "node_modules/universal-cookie": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.2.tgz", - "integrity": "sha512-GK9ygNUNk+u1umTtFoWItePuIGYy0TEu2w084mfjBpIIg9pikcN18EM6IMt+9VJCyR3uftu3yF2fFUMbwH1Kdw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz", + "integrity": "sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g==", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0" @@ -3753,9 +3751,9 @@ "dev": true }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -3862,13 +3860,13 @@ } }, "node_modules/vue-tsc": { - "version": "1.8.27", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", - "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.11.tgz", + "integrity": "sha512-dl5MEU4VGZdQFGBnKfPpAfV3SQmBDWs9o4YhUPvDmwk+zmb/RprzFJK2sagR6EWazogZhXENvykd3wBXWS9kng==", "dev": true, "dependencies": { - "@volar/typescript": "~1.11.1", - "@vue/language-core": "1.8.27", + "@volar/typescript": "~2.2.0-alpha.6", + "@vue/language-core": "2.0.11", "semver": "^7.5.4" }, "bin": { diff --git a/front-end/package.json b/front-end/package.json index 12b2c89..2141b26 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@headlessui/vue": "^1.7.17", - "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/27b487b6d0befdb2197a58ceadb1f1ac2b337786/@vnuge-vnlib.browser/release.tgz", + "@vnuge/vnlib.browser": "https://www.vaughnnugent.com/public/resources/software/builds/Plugins.Essentials/a7cf7c8987b8847984629293d8eb27908f3de3dd/@vnuge-vnlib.browser/release.tgz", "@vuelidate/core": "^2.0.2", "@vuelidate/validators": "^2.0.2", "@vueuse/core": "^10.3.x", @@ -47,6 +47,6 @@ "typescript": "^5.0.2", "vite": "^5.0.x", "vue-eslint-parser": "^9.3.0", - "vue-tsc": "^1.4.2" + "vue-tsc": "^2.0.x" } } diff --git a/front-end/src/App.vue b/front-end/src/App.vue index 4bd94c8..d1e9f50 100644 --- a/front-end/src/App.vue +++ b/front-end/src/App.vue @@ -73,7 +73,7 @@ const showIf = (tabId: TabId, active: TabId) => isEqual(tabId, active) </template> </SideMenuItem> - <SideMenuItem :tab="TabId.Login" :name="loggedIn ? 'Logout' : 'Login'"> + <SideMenuItem :tab="TabId.Login" :name="loggedIn ? 'Sign Out' : 'Sign In'"> <template #icon> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 7.5h11m0 0L8 3.786M12 7.5l-4 3.714M12 1h3c.53 0 1.04.196 1.414.544.375.348.586.82.586 1.313v9.286c0 .492-.21.965-.586 1.313A2.081 2.081 0 0 1 15 14h-3"/> </template> @@ -147,7 +147,7 @@ const showIf = (tabId: TabId, active: TabId) => isEqual(tabId, active) </template> </BottomMenuItem> - <BottomMenuItem :tab="TabId.Login" :name="loggedIn ? 'Logout' : 'Login'"> + <BottomMenuItem :tab="TabId.Login" :name="loggedIn ? 'Sign Out' : 'Sign In'"> <template #icon> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 7.5h11m0 0L8 3.786M12 7.5l-4 3.714M12 1h3c.53 0 1.04.196 1.414.544.375.348.586.82.586 1.313v9.286c0 .492-.21.965-.586 1.313A2.081 2.081 0 0 1 15 14h-3"/> </template> diff --git a/front-end/src/components/Bookmarks.vue b/front-end/src/components/Bookmarks.vue index 274b0b4..b83743c 100644 --- a/front-end/src/components/Bookmarks.vue +++ b/front-end/src/components/Bookmarks.vue @@ -1,31 +1,28 @@ <script setup lang="ts"> import { MaybeRef, Ref, computed, defineAsyncComponent, ref, shallowRef, watch } from 'vue'; import { useQuery, useStore } from '../store'; -import { get, set, formatTimeAgo, useToggle, useTimestamp, useFileDialog, asyncComputed, toReactive, useClipboard } from '@vueuse/core'; +import { get, set, useToggle, useFileDialog, asyncComputed, toReactive } from '@vueuse/core'; import { useVuelidate } from '@vuelidate/core'; import { required, maxLength, minLength, helpers } from '@vuelidate/validators'; -import { apiCall, useConfirm, useGeneralToaster, useVuelidateWrapper, useWait } from '@vnuge/vnlib.browser'; -import { clone, cloneDeep, join, defaultTo, every, filter, includes, isEmpty, isEqual, first, isString, chunk, map, forEach, isNil } from 'lodash-es'; +import { apiCall, useGeneralToaster, useVuelidateWrapper, useWait } from '@vnuge/vnlib.browser'; +import { clone, cloneDeep, defaultTo, every, filter, includes, isEmpty, isEqual, first, isString, chunk, map, forEach, isNil } from 'lodash-es'; import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' import { parseNetscapeBookmarkString } from './Boomarks/util.ts'; import type { BatchUploadResult, Bookmark, BookmarkError } from '../store/bookmarks'; import AddOrUpdateForm from './Boomarks/AddOrUpdateForm.vue'; +import BookmarkList from './Boomarks/BookmarkList.vue'; const Dialog = defineAsyncComponent(() => import('./global/Dialog.vue')); const store = useStore(); const { waiting } = useWait(); -const { reveal } = useConfirm(); const toaster = useGeneralToaster(); const bookmarks = computed(() => store.bookmarks.list); const tags = computed(() => store.bookmarks.allTags); -const now = useTimestamp({interval: 1000}); + const selectedTags = computed(() => store.bookmarks.tags); const localSearch = shallowRef<string>(store.bookmarks.query); const nextPageAvailable = computed(() => isEqual(bookmarks.value?.length, get(store.bookmarks.pages.currentPageSize))); -const { copy } = useClipboard() -//Refresh on page load -store.bookmarks.refresh(); const safeNameRegex = /^[a-zA-Z0-9_\-\|\., ]*$/; const safeUrlRegex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/; @@ -76,27 +73,6 @@ const clear = () => { set(localSearch, ''); } -const bmDelete = async (bookmark: Bookmark) => { - const { isCanceled } = await reveal({ - title: 'Delete bookmark', - text: `Are you sure you want to delete ${bookmark.Name} ?`, - }) - - if(isCanceled) return; - - apiCall(async ({ toaster }) => { - - await store.bookmarks.api.delete(bookmark); - - toaster.general.success({ - title: 'Bookmark deleted', - text: 'Bookmark has been deleted successfully' - }); - - store.bookmarks.refresh(); - }) -} - const isTagSelected = (tag: string, currentTags: MaybeRef<string[]>) => includes(get(currentTags), tag); const execSearch = () => store.bookmarks.query = get(localSearch); const clearTags = () => store.bookmarks.tags = []; @@ -441,43 +417,9 @@ const upload = (() => { </div> <div class="mx-auto sm:mt-2"> - <div class="grid h-full grid-cols-1 gap-1 leading-tight md:leading-normal"> - - <div v-for="bm in bookmarks" :key="bm.Id" :id="join(['bm', bm.Id], '-')" class="w-full p-1"> - <div class=""> - <a class="bl-link" :href="bm.Url" target="_blank"> - {{ bm.Name }} - </a> - </div> - <div class="flex flex-row items-center"> - <span v-for="tag in bm.Tags"> - <span class="mr-1 text-sm text-teal-500 cursor-pointer dark:text-teal-300" @click="toggleTag(tag)"> - #{{ tag }} - </span> - </span> - <p class="ml-2 text-sm text-gray-500 truncate dark:text-gray-400 text-ellipsis"> - {{ bm.Description }} - </p> - </div> - <div class=""> - <span class="text-xs text-gray-500 dark:text-gray-400"> - {{ formatTimeAgo(new Date(bm.Created), {}, now) }} - </span> - | - <span class="inline-flex gap-1.5"> - <button class="text-xs text-gray-700 dark:text-gray-400" @click="copy(bm.Url)"> - Copy - </button> - <button class="text-xs text-gray-700 dark:text-gray-400" @click="edit.editBookmark(bm)"> - Edit - </button> - <button class="text-xs text-gray-700 dark:text-gray-400" @click="bmDelete(bm)"> - Delete - </button> - </span> - </div> - </div> - </div> + + <BookmarkList @toggle-tag="toggleTag" @edit="edit.editBookmark" /> + <div class="pr-4 mt-5 mb-10 ml-auto w-fit"> <div class="flex flex-col items-center"> <div class="text-sm"> @@ -600,9 +542,6 @@ const upload = (() => { Upload </button> </div> - <div class=""> - - </div> </form> </div> </div> diff --git a/front-end/src/components/Boomarks/AddOrUpdateForm.vue b/front-end/src/components/Boomarks/AddOrUpdateForm.vue index 0370e0c..d6ea4bc 100644 --- a/front-end/src/components/Boomarks/AddOrUpdateForm.vue +++ b/front-end/src/components/Boomarks/AddOrUpdateForm.vue @@ -1,8 +1,10 @@ <script setup lang="ts"> -import { computed, toRefs } from 'vue'; -import { isEmpty, join, split } from 'lodash-es'; +import { computed, shallowRef, toRefs } from 'vue'; +import { set, watchDebounced } from '@vueuse/core' +import { isEmpty, join, noop, split } from 'lodash-es'; import { useStore } from '../../store'; -import { useWait } from '@vnuge/vnlib.browser'; +import { WebMessage, useWait } from '@vnuge/vnlib.browser'; +import { AxiosError } from 'axios'; const emit = defineEmits(['submit']) const props = defineProps<{ @@ -20,6 +22,8 @@ const tags = computed({ const { websiteLookup:lookup } = useStore() const { setWaiting, waiting } = useWait() +const errMessage = shallowRef(); + const execLookup = async () => { //url must be valid before searching if(v$.value.Url.$invalid) return @@ -45,9 +49,10 @@ const execLookup = async () => { v$.value.Tags.$dirty = true; } } - catch(e){ - //Mostly ignore errors + catch(e){ console.error(e) + const res = (e as AxiosError).response?.data; + set(errMessage, (res as WebMessage)?.result); } finally{ setWaiting(false) @@ -56,6 +61,9 @@ const execLookup = async () => { const showSearchButton = computed(() => lookup.isSupported && !isEmpty(v$.value.Url.$model)) +//Clear error message after 5 seconds +watchDebounced(errMessage, v => v ? setTimeout(() => set(errMessage, ''), 5000) : noop()) + </script> <template> <form id="bm-add-or-update-form" class="grid grid-cols-1 gap-4 p-4" @submit.prevent="emit('submit')"> @@ -67,22 +75,31 @@ const showSearchButton = computed(() => lookup.isSupported && !isEmpty(v$.value. <input type="text" id="url" class="input" placeholder="https://www.example.com" v-model="v$.Url.$model" :class="{'dirty': v$.Url.$dirty, 'error': v$.Url.$invalid}" required> - <div class=""> - <button - type="button" - :disabled="!showSearchButton || waiting" - @click.self.prevent="execLookup" - id="search-btn" - class="btn blue search-btn" - > - <svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" - viewBox="0 0 20 20"> - <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" /> - </svg> + <div class="my-auto"> + <button type="button" :disabled="!showSearchButton || waiting" @click.prevent="execLookup" + id="search-btn" class="btn blue search-btn"> + <span v-if="waiting" class="mx-auto"> + <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4 animate-spin" viewBox="0 0 15 15"> + <path fill="currentColor" fill-rule="evenodd" + d="M8 .5V5H7V.5h1ZM5.146 5.854l-3-3l.708-.708l3 3l-.708.708Zm4-.708l3-3l.708.708l-3 3l-.708-.708Zm.855 1.849L14.5 7l-.002 1l-4.5-.006l.002-1Zm-9.501 0H5v1H.5v-1Zm5.354 2.859l-3 3l-.708-.708l3-3l.708.708Zm6.292 3l-3-3l.708-.708l3 3l-.708.708ZM8 10v4.5H7V10h1Z" + clip-rule="evenodd" /> + </svg> + </span> + <span v-else> + <svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" + viewBox="0 0 20 20"> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" + stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" /> + </svg> + </span> </button> </div> </div> + <div v-if="errMessage" class="pl-2"> + <p class="text-xs italic text-red-800 dark:text-red-500"> + {{ errMessage }} + </p> + </div> </fieldset> <fieldset> <label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Title</label> diff --git a/front-end/src/components/Boomarks/BookmarkList.vue b/front-end/src/components/Boomarks/BookmarkList.vue new file mode 100644 index 0000000..ac0d49b --- /dev/null +++ b/front-end/src/components/Boomarks/BookmarkList.vue @@ -0,0 +1,157 @@ +<script setup lang="ts"> +import { type Bookmark } from '../../store/bookmarks'; + +import { computed, ref } from 'vue'; +import { useStore } from '../../store'; +import { formatTimeAgo, useTimestamp, useClipboard } from '@vueuse/core'; +import { apiCall, useConfirm } from '@vnuge/vnlib.browser'; +import { join, truncate } from 'lodash-es'; + +const emit = defineEmits(['toggleTag', 'edit']); + +const store = useStore(); +const bookmarks = computed(() => store.bookmarks.list); +const readable = ref(true); //Future allow users to switch between clean and readable layout + +//Refresh on page load +store.bookmarks.refresh(); + +const { copy } = useClipboard() +const { reveal } = useConfirm(); +const now = useTimestamp({ interval: 1000 }); + +const bmDelete = async (bookmark: Bookmark) => { + const { isCanceled } = await reveal({ + title: 'Delete bookmark', + text: `Are you sure you want to delete ${bookmark.Name} ?`, + }) + + if (isCanceled) return; + + apiCall(async ({ toaster }) => { + + await store.bookmarks.api.delete(bookmark); + + toaster.general.success({ + title: 'Bookmark deleted', + text: 'Bookmark has been deleted successfully' + }); + + store.bookmarks.refresh(); + }) +} + +const truncatText = (desc: string) => truncate(desc, { length: 100 }); + +</script> + +<template> + <div class="grid h-full grid-cols-1 gap-0"> + <div v-for="bm in bookmarks" :key="bm.Id" :id="join(['bm', bm.Id], '-')" class="w-full p-1"> + <div v-if="readable" class="leading-tight md:leading-normal"> + <div class=""> + <a class="bl-link" :href="bm.Url" target="_blank"> + {{ bm.Name }} + </a> + </div> + <div class="flex flex-row items-center"> + <span v-for="tag in bm.Tags"> + <span class="mr-1 text-sm text-teal-500 cursor-pointer dark:text-teal-300" + @click="emit('toggleTag', tag)"> + #{{ tag }} + </span> + </span> + <p class="ml-2 text-sm text-gray-500 truncate dark:text-gray-400 text-ellipsis"> + {{ bm.Description }} + </p> + </div> + <div class="flex items-center gap-1.5"> + <span class="text-xs text-gray-500 dark:text-gray-400"> + {{ formatTimeAgo(new Date(bm.Created), {}, now) }} + </span> + | + <span class="flex flex-row gap-1.5"> + <button class="text-xs text-gray-700 dark:text-gray-400" @click="copy(bm.Url)"> + Copy + </button> + <button class="text-xs text-gray-700 dark:text-gray-400" @click="emit('edit', bm)"> + Edit + </button> + <button class="text-xs text-gray-700 dark:text-gray-400" @click="bmDelete(bm)"> + Delete + </button> + </span> + </div> + </div> + <div v-else class="leading-tight clean-layout"> + <div class="flex flex-row"> + <div class="flex-1"> + <a class="text-sm font-bold bl-link" :href="bm.Url" target="_blank"> + {{ bm.Name }} + </a> + </div> + <div class=""> + <span class="inline-flex gap-1"> + <button class="text-xs text-gray-700 dark:text-gray-400" @click="copy(bm.Url)"> + <svg class="w-5 h-5 text-gray-800 dark:text-white" aria-hidden="true" + xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" + viewBox="0 0 24 24"> + <path stroke="currentColor" stroke-linejoin="round" stroke-width="2" + d="M9 8v3a1 1 0 0 1-1 1H5m11 4h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-7a1 1 0 0 0-1 1v1m4 3v10a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-7.13a1 1 0 0 1 .24-.65L7.7 8.35A1 1 0 0 1 8.46 8H13a1 1 0 0 1 1 1Z" /> + </svg> + </button> + <button class="text-xs text-gray-700 dark:text-gray-400" @click="emit('edit', bm)"> + <svg class="w-5 h-5 text-gray-800 dark:text-white" aria-hidden="true" + xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" + viewBox="0 0 24 24"> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" + stroke-width="2" + d="m14.304 4.844 2.852 2.852M7 7H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-4.5m2.409-9.91a2.017 2.017 0 0 1 0 2.853l-6.844 6.844L8 14l.713-3.565 6.844-6.844a2.015 2.015 0 0 1 2.852 0Z" /> + </svg> + </button> + <button class="text-xs text-gray-700 dark:text-gray-400 " @click="bmDelete(bm)"> + <svg class="w-5 h-5 text-gray-800 duration-100 ease-in dark:text-white trash" + aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" + fill="none" viewBox="0 0 24 24"> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" + stroke-width="2" + d="M5 7h14m-9 3v8m4-8v8M10 3h4a1 1 0 0 1 1 1v3H9V4a1 1 0 0 1 1-1ZM6 7h12v13a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7Z" /> + </svg> + </button> + </span> + </div> + </div> + <div class="flex flex-row items-start"> + <p class="text-sm text-gray-500 dark:text-gray-300 max-w-[26rem] flex-auto"> + {{ truncatText(bm.Description) }} + </p> + <div class="flex flex-col flex-wrap items-end ml-5 class gap-x-2 max-h-16"> + <span v-for="tag in bm.Tags"> + <span class="mr-1 text-xs text-gray-500 duration-75 ease-linear cursor-pointer dark:text-gray-500 hover:text-teal-500 hover:dark:text-teal-400" + @click="emit('toggleTag', tag)"> + {{ tag }} + </span> + </span> + </div> + </div> + </div> + </div> + </div> +</template> + +<style lang="scss" scoped> + .clean-layout { + @apply shadow-sm md:px-6 p-3 border rounded-md h-[7rem]; + @apply bg-white dark:bg-gray-800 dark:border-gray-700 max-w-[40rem]; + + button svg{ + &:hover{ + @apply text-gray-500 dark:text-gray-300 ease-linear duration-75; + + &.trash{ + @apply hover:text-red-500; + } + } + } + } +</style>
\ No newline at end of file diff --git a/front-end/src/components/Login/AdminReg.vue b/front-end/src/components/Login/AdminReg.vue index 8546512..f9bbbb8 100644 --- a/front-end/src/components/Login/AdminReg.vue +++ b/front-end/src/components/Login/AdminReg.vue @@ -74,10 +74,22 @@ const onSubmit = async () => { <input type="password" name="repeat-password" id="repeat-password" class="input" placeholder="••••••••" required v-model="v$.repeat.$model"> </fieldset> - <button form="admin-registation" type="submit" class="btn">Register</button> + <button type="submit" for="admin-registation" class="flex justify-center btn"> + <span v-if="waiting" class="mx-auto animate-spin"> + <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 15 15"> + <path fill="currentColor" fill-rule="evenodd" + d="M8 .5V5H7V.5h1ZM5.146 5.854l-3-3l.708-.708l3 3l-.708.708Zm4-.708l3-3l.708.708l-3 3l-.708-.708Zm.855 1.849L14.5 7l-.002 1l-4.5-.006l.002-1Zm-9.501 0H5v1H.5v-1Zm5.354 2.859l-3 3l-.708-.708l3-3l.708.708Zm6.292 3l-3-3l.708-.708l3 3l-.708.708ZM8 10v4.5H7V10h1Z" + clip-rule="evenodd" /> + </svg> + </span> + <span v-else> + Register + </span> + </button> </form> <p class="py-4 text-sm text-red-500"> - This tab is only visible when the server is in setup mode. You can create as many admin accounts as you like now. + This tab is only visible when the server is in setup mode. You can create as many admin accounts as you like + now. </p> </template> diff --git a/front-end/src/components/Login/PkiLogin.vue b/front-end/src/components/Login/PkiLogin.vue index 4515062..d3a635e 100644 --- a/front-end/src/components/Login/PkiLogin.vue +++ b/front-end/src/components/Login/PkiLogin.vue @@ -1,12 +1,13 @@ <script setup lang="ts"> import { isEmpty } from 'lodash-es'; -import { apiCall, debugLog, useMessage } from '@vnuge/vnlib.browser'; +import { apiCall, debugLog, useMessage, useWait } from '@vnuge/vnlib.browser'; import { ref } from 'vue' import { decodeJwt } from 'jose' import { useStore } from '../../store'; const { setMessage } = useMessage() const { pkiAuth } = useStore() +const { waiting } = useWait() const otp = ref('') @@ -32,19 +33,27 @@ const onSubmit = () => { </script> <template> - <form id="pki-login-form" class="max-w-sm mx-auto" @submit.prevent="onSubmit"> - <label for="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"> - Paste your one time password (OTP) - </label> - <textarea - id="message" rows="5" - v-model="otp" - class="block p-2.5 w-full text-sm text-gray-900 bg-transparent rounded border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" - placeholder="Enter your OTP" - > + <form id="pki-login-form" class="max-w-sm mx-auto" @submit.prevent="onSubmit"> + <label for="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"> + Paste your one time password (OTP) + </label> + <textarea id="message" rows="5" v-model="otp" + class="block p-2.5 w-full text-sm text-gray-900 bg-transparent rounded border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + placeholder="Enter your OTP"> </textarea> - - <button type="submit" for="pki-login-form" class="mt-4 btn">Submit</button> - - </form> + + <button type="submit" for="pki-login-form" class="flex justify-center mt-4 btn"> + <span v-if="waiting" class="mx-auto animate-spin"> + <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 15 15"> + <path fill="currentColor" fill-rule="evenodd" + d="M8 .5V5H7V.5h1ZM5.146 5.854l-3-3l.708-.708l3 3l-.708.708Zm4-.708l3-3l.708.708l-3 3l-.708-.708Zm.855 1.849L14.5 7l-.002 1l-4.5-.006l.002-1Zm-9.501 0H5v1H.5v-1Zm5.354 2.859l-3 3l-.708-.708l3-3l.708.708Zm6.292 3l-3-3l.708-.708l3 3l-.708.708ZM8 10v4.5H7V10h1Z" + clip-rule="evenodd" /> + </svg> + </span> + <span v-else> + Submit + </span> + </button> + + </form> </template>
\ No newline at end of file diff --git a/front-end/src/components/Login/UserPass.vue b/front-end/src/components/Login/UserPass.vue index c47e594..9fd64f4 100644 --- a/front-end/src/components/Login/UserPass.vue +++ b/front-end/src/components/Login/UserPass.vue @@ -110,7 +110,18 @@ const onSubmit = async () => { v-model="v$.password.$model" > </fieldset> - <button type="submit" class="btn">Sign in</button> + <button type="submit" class="flex justify-center btn"> + <span v-if="waiting" class="mx-auto animate-spin"> + <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 15 15"> + <path fill="currentColor" fill-rule="evenodd" + d="M8 .5V5H7V.5h1ZM5.146 5.854l-3-3l.708-.708l3 3l-.708.708Zm4-.708l3-3l.708.708l-3 3l-.708-.708Zm.855 1.849L14.5 7l-.002 1l-4.5-.006l.002-1Zm-9.501 0H5v1H.5v-1Zm5.354 2.859l-3 3l-.708-.708l3-3l.708.708Zm6.292 3l-3-3l.708-.708l3 3l-.708.708ZM8 10v4.5H7V10h1Z" + clip-rule="evenodd" /> + </svg> + </span> + <span v-else> + Sign in + </span> + </button> </form> </template> diff --git a/front-end/src/components/global/Dialog.vue b/front-end/src/components/global/Dialog.vue index 65d9165..18ad0fe 100644 --- a/front-end/src/components/global/Dialog.vue +++ b/front-end/src/components/global/Dialog.vue @@ -22,7 +22,7 @@ onClickOutside(dialog, () => get(open) ? cancel() : noop()) class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-20 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"> <div class="fixed inset-0 bg-black/30" aria-hidden="true" /> - <div class="relative w-full max-w-xl max-h-full p-4 mx-auto mt-[8rem] md:mt-32"> + <div class="relative w-full max-w-xl max-h-full p-4 mx-auto mt-16 md:mt-32"> <!-- Modal content --> <div class="relative bg-white rounded shadow dark:bg-gray-700" ref="dialog"> <!-- Modal header --> diff --git a/front-end/src/index.scss b/front-end/src/index.scss index 5575eb3..0b86ea1 100644 --- a/front-end/src/index.scss +++ b/front-end/src/index.scss @@ -18,7 +18,7 @@ .input { @apply bg-transparent border border-gray-300 text-gray-900 text-sm rounded focus:ring-blue-500 focus:border-blue-500 block w-full p-2 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500; - &.dirty{ + &.dirty:focus{ @apply bg-green-50 border-green-500 text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 focus:ring-green-500 focus:border-green-500 dark:bg-gray-700 dark:border-green-500; } diff --git a/front-end/src/main.ts b/front-end/src/main.ts index 2f2ca8e..eeac96d 100644 --- a/front-end/src/main.ts +++ b/front-end/src/main.ts @@ -68,7 +68,7 @@ store.use(profilePlugin('/account/profile')) //Enable mfa with totp settings plugin (optional pki config) .use(mfaSettingsPlugin('/account/mfa', '/account/pki')) //Setup social mfa plugin - .use(socialMfaPlugin("/account/social/portals")) + .use(socialMfaPlugin("/login/social/portals")) //Add the oauth2 apps plugin .use(bookmarkPlugin('/bookmarks')) .use(siteLookupPlugin('/lookup', 2000)) diff --git a/front-end/src/store/socialMfaPlugin.ts b/front-end/src/store/socialMfaPlugin.ts index e0ec972..79cb088 100644 --- a/front-end/src/store/socialMfaPlugin.ts +++ b/front-end/src/store/socialMfaPlugin.ts @@ -1,4 +1,3 @@ - import 'pinia' import { MaybeRef } from 'vue'; import { |