Преглед на файлове

fix: various fixes + improvements

NGPixel преди 1 година
родител
ревизия
4dcc9396d5

+ 0 - 1
.devcontainer/devcontainer.json

@@ -38,7 +38,6 @@
         "editorconfig.editorconfig",
         "lokalise.i18n-ally",
         "mrmlnc.vscode-duplicate",
-        "mtxr.sqltools-driver-pg",
         "mutantdino.resourcemonitor",
         "wayou.vscode-todo-highlight",
         "GraphQL.vscode-graphql"

+ 1 - 1
.devcontainer/docker-compose.yml

@@ -28,7 +28,7 @@ services:
     # (Adding the "ports" property to this file will not forward from a Codespace.)
 
   db:
-    image: postgres:16rc1
+    image: postgres:16
     restart: unless-stopped
     volumes:
       - postgres-data:/var/lib/postgresql/data

+ 49 - 7
blocks/block-index/component.js

@@ -16,6 +16,15 @@ export class BlockIndexElement extends LitElement {
         padding: 0;
         margin: 0;
         list-style: none;
+        display: grid;
+        grid-auto-flow: row;
+        grid-template-columns: repeat(1, minmax(0, 1fr));
+        gap: 0.5rem;
+      }
+      @media (min-width: 1024px) {
+        ul {
+          grid-template-columns: repeat(2, minmax(0, 1fr));
+        }
       }
 
       li {
@@ -28,6 +37,9 @@ export class BlockIndexElement extends LitElement {
         padding: 0;
         border-radius: 5px;
         font-weight: 500;
+        display: flex;
+        align-items: stretch;
+        justify-content: stretch;
       }
       :host-context(body.body--dark) li {
         background-color: #222;
@@ -47,15 +59,39 @@ export class BlockIndexElement extends LitElement {
         background-image: linear-gradient(to bottom,#1e232a, #161b22);
         border-left-color: var(--q-primary);
       }
-      li + li {
-        margin-top: .5rem;
-      }
       li a {
-        display: block;
+        display: flex;
         color: var(--q-primary);
         padding: 1rem;
         text-decoration: none;
+        flex: 1;
+        flex-direction: column;
+        justify-content: center;
+        position: relative;
+      }
+      li a > span {
+        display: block;
+        color: #666;
+        font-size: .8em;
+        font-weight: normal;
+        pointer-events: none;
       }
+      li a > svg {
+        width: 32px;
+        position: absolute;
+        right: 16px;
+        pointer-events: none;
+      }
+      li a > svg path {
+        fill: rgba(0,0,0,.2);
+      }
+      :host-context(body.body--dark) li a > svg path {
+        fill: rgba(255,255,255,.2);
+      }
+      li:hover a > svg path, :host-context(body.body--dark) li:hover a > svg path {
+        fill: color-mix(in srgb, currentColor 50%, transparent);
+      }
+
       .no-links {
         color: var(--q-negative);
         border: 1px dashed color-mix(in srgb, currentColor 50%, transparent);
@@ -158,13 +194,19 @@ export class BlockIndexElement extends LitElement {
     return this._pages.length > 0 || this._loading ? html`
       <ul>
         ${this._pages.map(p =>
-          html`<li><a href="${p.href}" @click="${this._navigate}">${p.title}</a></li>`
+          html`<li>
+            <a href="${p.href}" @click="${this._navigate}">
+              ${p.title}
+              ${p.description ? html`<span>${p.description}</span>` : null}
+              <svg xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 48 48" width="48px" height="48px">
+                <path d="M 24 4 C 12.972292 4 4 12.972292 4 24 C 4 32.465211 9.2720863 39.722981 16.724609 42.634766 A 1.50015 1.50015 0 1 0 17.816406 39.841797 C 11.48893 37.369581 7 31.220789 7 24 C 7 14.593708 14.593708 7 24 7 A 1.50015 1.50015 0 1 0 24 4 z M 32.734375 6.1816406 A 1.50015 1.50015 0 0 0 32.033203 9.0136719 C 37.368997 11.880008 41 17.504745 41 24 C 41 33.406292 33.406292 41 24 41 A 1.50015 1.50015 0 1 0 24 44 C 35.027708 44 44 35.027708 44 24 C 44 16.385255 39.733331 9.7447579 33.453125 6.3710938 A 1.50015 1.50015 0 0 0 32.734375 6.1816406 z M 25.484375 16.484375 A 1.50015 1.50015 0 0 0 24.439453 19.060547 L 27.878906 22.5 L 16.5 22.5 A 1.50015 1.50015 0 1 0 16.5 25.5 L 27.878906 25.5 L 24.439453 28.939453 A 1.50015 1.50015 0 1 0 26.560547 31.060547 L 32.560547 25.060547 A 1.50015 1.50015 0 0 0 32.560547 22.939453 L 26.560547 16.939453 A 1.50015 1.50015 0 0 0 25.484375 16.484375 z"/>
+              </svg>
+            </a>
+          </li>`
         )}
       </ul>
-      <slot></slot>
     ` : html`
       <div class="no-links">${this.noResultMsg}</div>
-      <slot></slot>
     `
   }
 

+ 3 - 0
blocks/block-index/tree.graphql

@@ -23,5 +23,8 @@ query blockIndexFetchPages (
     folderPath
     fileName
     title
+    ...on TreeItemPage {
+      description
+    }
   }
 }

+ 1 - 1
dev/build/Dockerfile

@@ -18,7 +18,7 @@ RUN mkdir -p /wiki && \
 WORKDIR /wiki
 
 COPY --chown=node:node ./assets ./assets
-COPY --chown=node:node ./blocks/dist ./blocks/dist
+COPY --chown=node:node ./blocks/compiled ./blocks/compiled
 COPY --chown=node:node ./server ./server
 COPY --chown=node:node ./dev/build/config.yml ./config.yml
 COPY --chown=node:node ./LICENSE ./LICENSE

+ 4 - 4
server/graph/resolvers/storage.mjs

@@ -164,7 +164,7 @@ export default {
         }
         // await WIKI.db.storage.initTargets()
         return {
-          status: generateSuccess('Storage targets updated successfully')
+          operation: generateSuccess('Storage targets updated successfully')
         }
       } catch (err) {
         return generateError(err)
@@ -186,7 +186,7 @@ export default {
         const result = await WIKI.storage.modules[md.key].setup(args.targetId, args.state)
 
         return {
-          status: generateSuccess('Storage target setup step succeeded'),
+          operation: generateSuccess('Storage target setup step succeeded'),
           state: result
         }
       } catch (err) {
@@ -209,7 +209,7 @@ export default {
         await WIKI.storage.modules[md.key].setupDestroy(args.targetId)
 
         return {
-          status: generateSuccess('Storage target setup configuration destroyed succesfully.')
+          operation: generateSuccess('Storage target setup configuration destroyed succesfully.')
         }
       } catch (err) {
         return generateError(err)
@@ -219,7 +219,7 @@ export default {
       try {
         await WIKI.db.storage.executeAction(args.targetKey, args.handler)
         return {
-          status: generateSuccess('Action completed.')
+          operation: generateSuccess('Action completed.')
         }
       } catch (err) {
         return generateError(err)

+ 3 - 0
server/graph/resolvers/tree.mjs

@@ -117,6 +117,9 @@ export default {
           fileSize: item.meta?.fileSize || 0,
           fileExt: item.meta?.fileExt || '',
           mimeType: item.meta?.mimeType || ''
+        },
+        ...(item.type === 'page') && {
+          description: item.meta?.description || ''
         }
       }))
     },

+ 1 - 0
server/graph/schemas/tree.graphql

@@ -113,6 +113,7 @@ type TreeItemPage implements TreeItem {
   editor: String
   pageType: String
   title: String
+  description: String
   updatedAt: Date
 }
 

+ 2 - 2
server/modules/storage/github/storage.js → server/modules/storage/github/storage.mjs

@@ -1,6 +1,6 @@
-const { Octokit, App } = require('octokit')
+import { Octokit, App } from 'octokit'
 
-module.exports = {
+export default {
   async activated () { },
   async deactivated () { },
   async init () { },

Файловите разлики са ограничени, защото са твърде много
+ 583 - 104
server/package-lock.json


+ 35 - 34
server/package.json

@@ -9,7 +9,9 @@
   "dev": true,
   "scripts": {
     "start": "cd .. && node server",
-    "dev": "cd .. && nodemon server --watch server --ext mjs,js,json,graphql,gql"
+    "dev": "cd .. && nodemon server --watch server --ext mjs,js,json,graphql,gql",
+    "ncu": "ncu -i -x codemirror,codemirror-asciidoc",
+    "ncu-u": "ncu -u -x codemirror,codemirror-asciidoc"
   },
   "repository": {
     "type": "git",
@@ -34,20 +36,19 @@
     "node": ">=18.0"
   },
   "dependencies": {
-    "@apollo/server": "4.7.5",
-    "@azure/storage-blob": "12.14.0",
+    "@apollo/server": "4.9.3",
+    "@azure/storage-blob": "12.16.0",
     "@exlinc/keycloak-passport": "1.0.2",
     "@graphql-tools/schema": "10.0.0",
     "@graphql-tools/utils": "10.0.1",
-    "@joplin/turndown-plugin-gfm": "1.0.49",
+    "@joplin/turndown-plugin-gfm": "1.0.50",
     "@root/csr": "0.8.1",
     "@root/keypairs": "0.10.3",
     "@root/pem": "1.0.4",
     "acme": "3.0.3",
     "akismet-api": "6.0.0",
-    "aws-sdk": "2.1409.0",
+    "aws-sdk": "2.1463.0",
     "bcryptjs": "2.4.3",
-    "body-parser": "1.20.2",
     "chalk": "5.3.0",
     "cheerio": "1.0.0-rc.12",
     "chokidar": "3.5.3",
@@ -58,13 +59,13 @@
     "connect-session-knex": "3.0.1",
     "cookie-parser": "1.4.6",
     "cors": "2.8.5",
-    "cron-parser": "4.8.1",
+    "cron-parser": "4.9.0",
     "cuint": "0.2.2",
     "custom-error-instance": "2.1.2",
     "dependency-graph": "0.11.0",
     "diff": "5.1.0",
-    "diff2html": "3.4.35",
-    "dompurify": "3.0.4",
+    "diff2html": "3.4.43",
+    "dompurify": "3.0.5",
     "dotize": "0.3.0",
     "emoji-regex": "10.2.1",
     "eventemitter2": "6.4.9",
@@ -72,30 +73,30 @@
     "express-brute": "1.0.1",
     "express-session": "1.17.3",
     "file-type": "18.5.0",
-    "filesize": "10.0.7",
+    "filesize": "10.0.12",
     "fs-extra": "11.1.1",
     "getos": "3.2.1",
-    "graphql": "16.7.1",
+    "graphql": "16.8.1",
     "graphql-list-fields": "2.0.2",
-    "graphql-rate-limit-directive": "2.0.3",
+    "graphql-rate-limit-directive": "2.0.4",
     "graphql-tools": "9.0.0",
     "graphql-upload": "16.0.2",
     "he": "1.2.0",
     "highlight.js": "11.8.0",
-    "i18next": "23.2.6",
+    "i18next": "23.5.1",
     "i18next-node-fs-backend": "2.1.3",
     "image-size": "1.0.2",
     "js-base64": "3.7.5",
     "js-binary": "1.2.0",
     "js-yaml": "4.1.0",
     "jsdom": "22.1.0",
-    "jsonwebtoken": "9.0.0",
+    "jsonwebtoken": "9.0.2",
     "katex": "0.16.8",
     "klaw": "4.1.0",
-    "knex": "2.4.2",
+    "knex": "2.5.1",
     "lodash": "4.17.21",
     "lodash-es": "4.17.21",
-    "luxon": "3.3.0",
+    "luxon": "3.4.3",
     "markdown-it": "13.0.1",
     "markdown-it-abbr": "1.0.4",
     "markdown-it-attrs": "4.1.6",
@@ -105,8 +106,8 @@
     "markdown-it-footnote": "3.0.3",
     "markdown-it-imsize": "2.0.1",
     "markdown-it-mark": "3.0.1",
-    "markdown-it-mdc": "0.1.3",
-    "markdown-it-multimd-table": "4.2.2",
+    "markdown-it-mdc": "0.1.4",
+    "markdown-it-multimd-table": "4.2.3",
     "markdown-it-sub": "1.0.0",
     "markdown-it-sup": "1.0.0",
     "markdown-it-task-lists": "2.1.1",
@@ -114,11 +115,12 @@
     "mime-types": "2.1.35",
     "ms": "2.1.3",
     "multer": "1.4.4",
-    "nanoid": "4.0.2",
+    "nanoid": "5.0.1",
     "node-2fa": "2.0.3",
     "node-cache": "5.1.2",
-    "nodemailer": "6.9.3",
-    "objection": "3.0.4",
+    "nodemailer": "6.9.5",
+    "objection": "3.1.1",
+    "octokit": "3.1.0",
     "passport": "0.6.0",
     "passport-auth0": "1.4.3",
     "passport-azure-ad": "4.3.5",
@@ -140,46 +142,45 @@
     "passport-slack-oauth2": "1.2.0",
     "passport-twitch-strategy": "2.2.0",
     "pem-jwk": "2.0.0",
-    "pg": "8.11.1",
+    "pg": "8.11.3",
     "pg-hstore": "2.3.4",
     "pg-pubsub": "0.8.1",
-    "pg-query-stream": "4.5.1",
+    "pg-query-stream": "4.5.3",
     "pg-tsquery": "8.4.1",
-    "poolifier": "2.6.5",
+    "poolifier": "2.7.1",
     "punycode": "2.3.0",
-    "puppeteer-core": "20.7.4",
+    "puppeteer-core": "21.3.4",
     "qr-image": "3.2.0",
-    "rate-limiter-flexible": "2.4.1",
     "remove-markdown": "0.5.0",
     "request": "2.88.2",
     "request-promise": "4.2.6",
     "safe-regex": "2.1.1",
     "sanitize-filename": "1.6.3",
     "scim-query-filter-parser": "2.0.4",
-    "semver": "7.5.3",
+    "semver": "7.5.4",
     "serve-favicon": "2.5.0",
-    "sharp": "0.32.1",
+    "sharp": "0.32.6",
     "simple-git": "3.19.1",
-    "socket.io": "4.7.1",
+    "socket.io": "4.7.2",
     "striptags": "3.2.0",
-    "tar-fs": "3.0.3",
+    "tar-fs": "3.0.4",
     "turndown": "7.1.2",
     "twemoji": "14.0.2",
     "uslug": "1.0.4",
-    "uuid": "9.0.0",
+    "uuid": "9.0.1",
     "validate.js": "0.13.1",
     "xss": "1.0.14",
     "yargs": "17.7.2"
   },
   "devDependencies": {
-    "eslint": "8.44.0",
+    "eslint": "8.50.0",
     "eslint-config-requarks": "1.0.7",
     "eslint-config-standard": "17.1.0",
-    "eslint-plugin-import": "2.27.5",
+    "eslint-plugin-import": "2.28.1",
     "eslint-plugin-node": "11.1.0",
     "eslint-plugin-promise": "6.1.1",
     "eslint-plugin-standard": "4.1.0",
-    "nodemon": "2.0.22"
+    "nodemon": "3.0.1"
   },
   "overrides": {
     "@graphql-tools/utils": "10.0.1"

+ 2 - 3
server/web.mjs

@@ -1,4 +1,3 @@
-import bodyParser from 'body-parser'
 import compression from 'compression'
 import cookieParser from 'cookie-parser'
 import cors from 'cors'
@@ -151,7 +150,7 @@ export async function init () {
   // GraphQL Server
   // ----------------------------------------
 
-  app.use(bodyParser.json({ limit: WIKI.config.bodyParserLimit || '1mb' }))
+  app.use(express.json({ limit: WIKI.config.bodyParserLimit || '5mb' }))
   await WIKI.servers.startGraphQL()
 
   // ----------------------------------------
@@ -175,7 +174,7 @@ export async function init () {
   app.set('views', path.join(WIKI.SERVERPATH, 'views'))
   app.set('view engine', 'pug')
 
-  app.use(bodyParser.urlencoded({ extended: false, limit: '1mb' }))
+  app.use(express.urlencoded({ extended: false, limit: '1mb' }))
 
   // ----------------------------------------
   // View accessible data

+ 1 - 1
server/worker.mjs

@@ -51,4 +51,4 @@ export default new ThreadWorker(async (job) => {
   const task = (await import(`./tasks/workers/${kebabCase(job.task)}.mjs`)).task
   await task(job)
   return true
-}, { async: true })
+})

Файловите разлики са ограничени, защото са твърде много
+ 242 - 305
ux/package-lock.json


+ 45 - 45
ux/package.json

@@ -9,41 +9,41 @@
     "dev": "quasar dev",
     "build": "quasar build",
     "lint": "eslint --ext .js,.vue ./",
-    "ncu": "ncu -x codemirror,codemirror-asciidoc",
+    "ncu": "ncu -i -x codemirror,codemirror-asciidoc",
     "ncu-u": "ncu -u -x codemirror,codemirror-asciidoc"
   },
   "dependencies": {
-    "@apollo/client": "3.8.2",
-    "@lezer/common": "1.0.4",
+    "@apollo/client": "3.8.4",
+    "@lezer/common": "1.1.0",
     "@mdi/font": "7.2.96",
     "@quasar/extras": "1.16.6",
-    "@tiptap/core": "2.1.8",
-    "@tiptap/extension-code-block": "2.1.8",
-    "@tiptap/extension-code-block-lowlight": "2.1.8",
-    "@tiptap/extension-color": "2.1.8",
-    "@tiptap/extension-dropcursor": "2.1.8",
-    "@tiptap/extension-font-family": "2.1.8",
-    "@tiptap/extension-gapcursor": "2.1.8",
-    "@tiptap/extension-hard-break": "2.1.8",
-    "@tiptap/extension-highlight": "2.1.8",
-    "@tiptap/extension-history": "2.1.8",
-    "@tiptap/extension-image": "2.1.8",
-    "@tiptap/extension-mention": "2.1.8",
-    "@tiptap/extension-placeholder": "2.1.8",
-    "@tiptap/extension-table": "2.1.8",
-    "@tiptap/extension-table-cell": "2.1.8",
-    "@tiptap/extension-table-header": "2.1.8",
-    "@tiptap/extension-table-row": "2.1.8",
-    "@tiptap/extension-task-item": "2.1.8",
-    "@tiptap/extension-task-list": "2.1.8",
-    "@tiptap/extension-text-align": "2.1.8",
-    "@tiptap/extension-text-style": "2.1.8",
-    "@tiptap/extension-typography": "2.1.8",
-    "@tiptap/pm": "2.1.8",
-    "@tiptap/starter-kit": "2.1.8",
-    "@tiptap/vue-3": "2.1.8",
+    "@tiptap/core": "2.1.11",
+    "@tiptap/extension-code-block": "2.1.11",
+    "@tiptap/extension-code-block-lowlight": "2.1.11",
+    "@tiptap/extension-color": "2.1.11",
+    "@tiptap/extension-dropcursor": "2.1.11",
+    "@tiptap/extension-font-family": "2.1.11",
+    "@tiptap/extension-gapcursor": "2.1.11",
+    "@tiptap/extension-hard-break": "2.1.11",
+    "@tiptap/extension-highlight": "2.1.11",
+    "@tiptap/extension-history": "2.1.11",
+    "@tiptap/extension-image": "2.1.11",
+    "@tiptap/extension-mention": "2.1.11",
+    "@tiptap/extension-placeholder": "2.1.11",
+    "@tiptap/extension-table": "2.1.11",
+    "@tiptap/extension-table-cell": "2.1.11",
+    "@tiptap/extension-table-header": "2.1.11",
+    "@tiptap/extension-table-row": "2.1.11",
+    "@tiptap/extension-task-item": "2.1.11",
+    "@tiptap/extension-task-list": "2.1.11",
+    "@tiptap/extension-text-align": "2.1.11",
+    "@tiptap/extension-text-style": "2.1.11",
+    "@tiptap/extension-typography": "2.1.11",
+    "@tiptap/pm": "2.1.11",
+    "@tiptap/starter-kit": "2.1.11",
+    "@tiptap/vue-3": "2.1.11",
     "apollo-upload-client": "17.0.0",
-    "browser-fs-access": "0.34.1",
+    "browser-fs-access": "0.35.0",
     "clipboard": "2.0.11",
     "codemirror": "5.65.11",
     "codemirror-asciidoc": "1.0.4",
@@ -59,7 +59,7 @@
     "katex": "0.16.8",
     "lodash-es": "4.17.21",
     "lowlight": "3.0.0",
-    "luxon": "3.4.2",
+    "luxon": "3.4.3",
     "markdown-it": "13.0.1",
     "markdown-it-abbr": "1.0.4",
     "markdown-it-attrs": "4.1.6",
@@ -69,13 +69,13 @@
     "markdown-it-footnote": "3.0.3",
     "markdown-it-imsize": "2.0.1",
     "markdown-it-mark": "3.0.1",
-    "markdown-it-mdc": "0.1.3",
+    "markdown-it-mdc": "0.1.4",
     "markdown-it-multimd-table": "4.2.3",
     "markdown-it-sub": "1.0.0",
     "markdown-it-sup": "1.0.0",
     "markdown-it-task-lists": "2.1.1",
     "mitt": "3.0.1",
-    "monaco-editor": "0.41.0",
+    "monaco-editor": "0.43.0",
     "pako": "2.1.0",
     "pinia": "2.1.6",
     "prosemirror-commands": "1.5.2",
@@ -85,36 +85,36 @@
     "prosemirror-schema-list": "1.3.0",
     "prosemirror-state": "1.4.3",
     "prosemirror-transform": "1.7.5",
-    "prosemirror-view": "1.31.7",
+    "prosemirror-view": "1.31.8",
     "pug": "3.0.2",
-    "quasar": "2.12.6",
+    "quasar": "2.12.7",
     "slugify": "1.6.6",
     "socket.io-client": "4.7.2",
     "sortablejs-vue3": "1.2.9",
     "tabulator-tables": "5.5.2",
     "tippy.js": "6.3.7",
     "twemoji": "14.0.2",
-    "uuid": "9.0.0",
-    "v-network-graph": "0.9.7",
+    "uuid": "9.0.1",
+    "v-network-graph": "0.9.8",
     "vue": "3.3.4",
-    "vue-i18n": "9.2.2",
-    "vue-router": "4.2.4",
+    "vue-i18n": "9.4.1",
+    "vue-router": "4.2.5",
     "vue3-otp-input": "0.4.1",
     "vuedraggable": "4.1.0",
-    "xterm": "5.2.1",
+    "xterm": "5.3.0",
     "zxcvbn": "4.4.2"
   },
   "devDependencies": {
-    "@intlify/unplugin-vue-i18n": "0.13.0",
-    "@quasar/app-vite": "1.5.0",
-    "@types/lodash": "4.14.197",
+    "@intlify/unplugin-vue-i18n": "1.2.0",
+    "@quasar/app-vite": "1.6.2",
+    "@types/lodash": "4.14.199",
     "@volar/vue-language-plugin-pug": "1.6.5",
-    "autoprefixer": "10.4.15",
+    "autoprefixer": "10.4.16",
     "browserlist": "latest",
-    "eslint": "8.48.0",
+    "eslint": "8.50.0",
     "eslint-config-standard": "17.1.0",
     "eslint-plugin-import": "2.28.1",
-    "eslint-plugin-n": "16.0.2",
+    "eslint-plugin-n": "16.1.0",
     "eslint-plugin-promise": "6.1.1",
     "eslint-plugin-vue": "9.17.0"
   },

+ 6 - 0
ux/src/components/EditorMarkdown.vue

@@ -62,6 +62,12 @@
         flat
         )
         q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertTabset') }}
+      q-btn(
+        icon='mdi-toy-brick-plus'
+        padding='sm sm'
+        flat
+        )
+        q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertBlock') }}
       q-btn(
         icon='mdi-chart-multiline'
         padding='sm sm'

+ 26 - 31
ux/src/components/GithubSetupInstallDialog.vue

@@ -1,46 +1,41 @@
 <template lang="pug">
-q-dialog(ref='dialog', @hide='onDialogHide', persistent)
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
   q-card(style='min-width: 350px; max-width: 550px;')
     q-card-section.card-header
       q-icon(name='img:/_assets/icons/ultraviolet-github.svg', left, size='sm')
-      span {{$t(`admin.storage.githubSetupInstallApp`)}}
+      span {{t(`admin.storage.githubSetupInstallApp`)}}
     q-card-section
-      .text-body2 {{$t(`admin.storage.githubSetupInstallAppInfo`)}}
-      .text-body2.q-mt-md: strong.text-deep-orange {{$t('admin.storage.githubSetupInstallAppSelect')}}
-      .text-body2.q-mt-md {{$t(`admin.storage.githubSetupInstallAppReturn`)}}
+      .text-body2 {{t(`admin.storage.githubSetupInstallAppInfo`)}}
+      .text-body2.q-mt-md: strong.text-deep-orange {{t('admin.storage.githubSetupInstallAppSelect')}}
+      .text-body2.q-mt-md {{t(`admin.storage.githubSetupInstallAppReturn`)}}
     q-card-actions.card-actions
       q-space
       q-btn(
         unelevated
-        :label='$t(`admin.storage.githubSetupContinue`)'
+        :label='t(`admin.storage.githubSetupContinue`)'
         color='positive'
         padding='xs md'
-        @click='confirm'
+        @click='onDialogOK'
         )
 </template>
 
-<script>
-
-export default {
-  emits: ['ok', 'hide'],
-  data () {
-    return {
-    }
-  },
-  methods: {
-    show () {
-      this.$refs.dialog.show()
-    },
-    hide () {
-      this.$refs.dialog.hide()
-    },
-    onDialogHide () {
-      this.$emit('hide')
-    },
-    confirm () {
-      this.$emit('ok')
-      this.hide()
-    }
-  }
-}
+<script setup>
+import { useI18n } from 'vue-i18n'
+import { useDialogPluginComponent, useQuasar } from 'quasar'
+
+// EMITS
+
+defineEmits([
+  ...useDialogPluginComponent.emits
+])
+
+// QUASAR
+
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
+const $q = useQuasar()
+
+// I18N
+
+const { t } = useI18n()
+
 </script>

+ 23 - 18
ux/src/components/GroupEditOverlay.vue

@@ -33,21 +33,21 @@ q-layout(view='hHh lpR fFf', container)
       )
   q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
     q-list(padding, v-show='!state.isLoading')
-      q-item(
-        v-for='sc of sections'
-        :key='`section-` + sc.key'
-        clickable
-        :to='{ params: { section: sc.key } }'
-        active-class='bg-primary text-white'
-        :disabled='sc.disabled'
-        )
-        q-item-section(side)
-          q-icon(:name='sc.icon', color='white')
-        q-item-section {{sc.text}}
-        q-item-section(side, v-if='sc.usersTotal')
-          q-badge(color='dark-3', :label='state.usersTotal')
-        q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
-          q-badge(color='dark-3', :label='state.group.rules.length')
+      template(v-for='sc of sections', :key='`section-` + sc.key')
+        q-item(
+          v-if='!(isGuestGroup && sc.excludeGuests)'
+          clickable
+          :to='{ params: { section: sc.key } }'
+          active-class='bg-primary text-white'
+          :disabled='sc.disabled'
+          )
+          q-item-section(side)
+            q-icon(:name='sc.icon', color='white')
+          q-item-section {{sc.text}}
+          q-item-section(side, v-if='sc.usersTotal')
+            q-badge(color='dark-3', :label='state.usersTotal')
+          q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
+            q-badge(color='dark-3', :label='state.group.rules.length')
   q-page-container
     q-page(v-if='state.isLoading')
     //- -----------------------------------------------------------------------
@@ -73,9 +73,10 @@ q-layout(view='hHh lpR fFf', container)
                     :rules='groupNameValidation'
                     hide-bottom-space
                     :aria-label='t(`admin.groups.name`)'
+                    :disable='isGuestGroup'
                     )
 
-            q-card.shadow-1.q-pb-sm.q-mt-md
+            q-card.shadow-1.q-pb-sm.q-mt-md(v-if='!isGuestGroup')
               q-card-section
                 .text-subtitle1 {{t('admin.groups.authBehaviors')}}
               q-item
@@ -551,8 +552,8 @@ const state = reactive({
 const sections = [
   { key: 'overview', text: t('admin.groups.overview'), icon: 'las la-users' },
   { key: 'rules', text: t('admin.groups.rules'), icon: 'las la-file-invoice', rulesTotal: true },
-  { key: 'permissions', text: t('admin.groups.permissions'), icon: 'las la-list-alt' },
-  { key: 'users', text: t('admin.groups.users'), icon: 'las la-user', usersTotal: true }
+  { key: 'permissions', text: t('admin.groups.permissions'), icon: 'las la-list-alt', excludeGuests: true },
+  { key: 'users', text: t('admin.groups.users'), icon: 'las la-user', usersTotal: true, excludeGuests: true }
 ]
 
 const usersHeaders = [
@@ -781,6 +782,10 @@ const usersTotalPages = computed(() => {
   return Math.ceil(state.usersTotal / state.usersPageSize)
 })
 
+const isGuestGroup = computed(() => {
+  return adminStore.overlayOpts.id === '10000000-0000-4000-8000-000000000001'
+})
+
 // WATCHERS
 
 watch(() => route.params.section, checkRoute)

+ 38 - 21
ux/src/pages/AdminDashboard.vue

@@ -33,6 +33,32 @@ q-page.admin-dashboard
             :disable='!userStore.can(`manage:sites`)'
             to='/_admin/sites'
             )
+    .col-12.col-sm-6.col-lg-3
+      q-card
+        q-card-section.admin-dashboard-card
+          img(src='/_assets/icons/fluent-people.svg')
+          div
+            strong {{ t('admin.groups.title') }}
+            small.text-positive {{adminStore.info.groupsTotal}}
+        q-separator
+        q-card-actions(align='right')
+          q-btn(
+            flat
+            color='primary'
+            icon='las la-plus-circle'
+            :label='t(`common.actions.new`)'
+            :disable='!userStore.can(`manage:users`)'
+            @click='newGroup'
+            )
+          q-separator.q-mx-sm(vertical)
+          q-btn(
+            flat
+            color='primary'
+            icon='las la-users'
+            :label='t(`common.actions.manage`)'
+            :disable='!userStore.can(`manage:users`)'
+            to='/_admin/groups'
+            )
     .col-12.col-sm-6.col-lg-3
       q-card
         q-card-section.admin-dashboard-card
@@ -54,7 +80,7 @@ q-page.admin-dashboard
           q-btn(
             flat
             color='primary'
-            icon='las la-users'
+            icon='las la-user-friends'
             :label='t(`common.actions.manage`)'
             :disable='!userStore.can(`manage:users`)'
             to='/_admin/users'
@@ -76,23 +102,6 @@ q-page.admin-dashboard
             :disable='!userStore.can(`manage:sites`)'
             :to='`/_admin/` + adminStore.currentSiteId + `/analytics`'
             )
-    .col-12.col-sm-6.col-lg-3
-      q-card
-        q-card-section.admin-dashboard-card
-          img(src='/_assets/icons/fluent-ssd-animated.svg')
-          div
-            strong {{ t('admin.storage.title') }}
-            small.text-positive Operational
-        q-separator
-        q-card-actions(align='right')
-          q-btn(
-            flat
-            color='primary'
-            icon='las la-server'
-            :label='t(`common.actions.manage`)'
-            :disable='!userStore.can(`manage:sites`)'
-            :to='`/_admin/` + adminStore.currentSiteId + `/storage`'
-            )
     .col-12
       q-banner.bg-positive.text-white(
         :class='adminStore.isVersionLatest ? `bg-positive` : `bg-warning`'
@@ -234,9 +243,10 @@ import { useUserStore } from 'src/stores/user'
 
 // COMPONENTS
 
-import CheckUpdateDialog from '../components/CheckUpdateDialog.vue'
-import SiteCreateDialog from '../components/SiteCreateDialog.vue'
-import UserCreateDialog from '../components/UserCreateDialog.vue'
+import CheckUpdateDialog from 'src/components/CheckUpdateDialog.vue'
+import SiteCreateDialog from 'src/components/SiteCreateDialog.vue'
+import UserCreateDialog from 'src/components/UserCreateDialog.vue'
+import GroupCreateDialog from 'src/components/GroupCreateDialog.vue'
 
 // QUASAR
 
@@ -277,6 +287,13 @@ function newUser () {
     router.push('/_admin/users')
   })
 }
+function newGroup () {
+  $q.dialog({
+    component: GroupCreateDialog
+  }).onOk(() => {
+    router.push('/_admin/groups')
+  })
+}
 function checkForUpdates () {
   $q.dialog({
     component: CheckUpdateDialog

+ 13 - 13
ux/src/pages/AdminStorage.vue

@@ -169,7 +169,7 @@ q-page.admin-storage
                   @click='setupGitHubStep(`verify`)'
                   :loading='state.setupCfg.loading'
                 )
-          q-card.q-pb-sm.q-mt-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state === `configured`')
+          q-card.q-pb-sm.q-mb-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state === `configured`')
             q-card-section
               .text-subtitle1 {{t('admin.storage.setup')}}
               .text-body2.text-grey {{ t('admin.storage.setupConfiguredHint') }}
@@ -839,7 +839,7 @@ async function save ({ silent }) {
             siteId: $siteId
             targets: $targets
           ) {
-            status {
+            operation {
               succeeded
               message
             }
@@ -861,7 +861,7 @@ async function save ({ silent }) {
         }))
       }
     })
-    if (resp?.data?.updateStorageTargets?.status?.succeeded) {
+    if (resp?.data?.updateStorageTargets?.operation?.succeeded) {
       saveSuccess = true
       if (!silent) {
         $q.notify({
@@ -870,7 +870,7 @@ async function save ({ silent }) {
         })
       }
     } else {
-      throw new Error(resp?.data?.updateStorageTargets?.status?.message || 'Unexpected error')
+      throw new Error(resp?.data?.updateStorageTargets?.operation?.message || 'Unexpected error')
     }
   } catch (err) {
     $q.notify({
@@ -970,7 +970,7 @@ async function setupDestroy () {
             destroyStorageTargetSetup(
               targetId: $targetId
             ) {
-              status {
+              operation {
                 succeeded
                 message
               }
@@ -981,7 +981,7 @@ async function setupDestroy () {
           targetId: state.selectedTarget
         }
       })
-      if (resp?.data?.destroyStorageTargetSetup?.status?.succeeded) {
+      if (resp?.data?.destroyStorageTargetSetup?.operation?.succeeded) {
         state.target.setup.state = 'notconfigured'
         setTimeout(() => {
           $q.loading.hide()
@@ -991,7 +991,7 @@ async function setupDestroy () {
           })
         }, 2000)
       } else {
-        throw new Error(resp?.data?.destroyStorageTargetSetup?.status?.message || 'Unexpected error')
+        throw new Error(resp?.data?.destroyStorageTargetSetup?.operation?.message || 'Unexpected error')
       }
     } catch (err) {
       $q.notify({
@@ -1085,7 +1085,7 @@ async function setupGitHubStep (step, code) {
             targetId: $targetId
             state: $state
           ) {
-            status {
+            operation {
               succeeded
               message
             }
@@ -1094,14 +1094,14 @@ async function setupGitHubStep (step, code) {
         }
       `,
       variables: {
-        targetId: this.selectedTarget,
+        targetId: state.selectedTarget,
         state: {
           step,
           ...code && { code }
         }
       }
     })
-    if (resp?.data?.setupStorageTarget?.status?.succeeded) {
+    if (resp?.data?.setupStorageTarget?.operation?.succeeded) {
       switch (resp.data.setupStorageTarget.state?.nextStep) {
         case 'installApp': {
           router.replace({ query: null })
@@ -1121,8 +1121,8 @@ async function setupGitHubStep (step, code) {
           break
         }
         case 'completed': {
-          this.target.isEnabled = true
-          this.target.setup.state = 'configured'
+          state.target.isEnabled = true
+          state.target.setup.state = 'configured'
           setTimeout(() => {
             $q.loading.hide()
             $q.notify({
@@ -1137,7 +1137,7 @@ async function setupGitHubStep (step, code) {
         }
       }
     } else {
-      throw new Error(resp?.data?.setupStorageTarget?.status?.message || 'Unexpected error')
+      throw new Error(resp?.data?.setupStorageTarget?.operation?.message || 'Unexpected error')
     }
   } catch (err) {
     $q.loading.hide()

+ 1 - 1
ux/src/renderers/markdown.js

@@ -60,6 +60,7 @@ export class MarkdownRenderer {
         }
       }
     })
+      .use(mdMdc)
       .use(mdAttrs, {
         allowedAttributes: ['id', 'class', 'target']
       })
@@ -73,7 +74,6 @@ export class MarkdownRenderer {
       .use(mdMark)
       .use(mdFootnote)
       .use(mdImsize)
-      .use(mdMdc)
 
     if (config.underline) {
       this.md.use(mdUnderline)

Някои файлове не бяха показани, защото твърде много файлове са промени