fengchang 10 månader sedan
förälder
incheckning
066b523869
82 ändrade filer med 10972 tillägg och 2 borttagningar
  1. 1 1
      hichina-main-back/src/main/resources/application-dev.properties
  2. 9 0
      hichina-main-front-mobile-first-v2/.editorconfig
  3. 7 0
      hichina-main-front-mobile-first-v2/.eslintignore
  4. 66 0
      hichina-main-front-mobile-first-v2/.eslintrc.cjs
  5. 33 0
      hichina-main-front-mobile-first-v2/.gitignore
  6. 3 0
      hichina-main-front-mobile-first-v2/.npmrc
  7. 15 0
      hichina-main-front-mobile-first-v2/.vscode/extensions.json
  8. 15 0
      hichina-main-front-mobile-first-v2/.vscode/settings.json
  9. 41 0
      hichina-main-front-mobile-first-v2/README.md
  10. 21 0
      hichina-main-front-mobile-first-v2/index.html
  11. 39 0
      hichina-main-front-mobile-first-v2/jsconfig.json
  12. 49 0
      hichina-main-front-mobile-first-v2/package.json
  13. 27 0
      hichina-main-front-mobile-first-v2/postcss.config.cjs
  14. BIN
      hichina-main-front-mobile-first-v2/public/favicon-old.ico
  15. BIN
      hichina-main-front-mobile-first-v2/public/favicon.ico
  16. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-128x128-old.png
  17. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-128x128.png
  18. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-16x16-old.png
  19. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-16x16.png
  20. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-32x32-old.png
  21. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-32x32.png
  22. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-96x96-old.png
  23. BIN
      hichina-main-front-mobile-first-v2/public/icons/favicon-96x96.png
  24. BIN
      hichina-main-front-mobile-first-v2/public/icons/logo.png
  25. 213 0
      hichina-main-front-mobile-first-v2/quasar.config.js
  26. 58 0
      hichina-main-front-mobile-first-v2/src-ssr/middlewares/render.js
  27. 136 0
      hichina-main-front-mobile-first-v2/src-ssr/server.js
  28. 10 0
      hichina-main-front-mobile-first-v2/src-ssr/ssr-flag.d.ts
  29. 11 0
      hichina-main-front-mobile-first-v2/src/App.vue
  30. BIN
      hichina-main-front-mobile-first-v2/src/assets/hichinalogo.png
  31. BIN
      hichina-main-front-mobile-first-v2/src/assets/profileplaceholder.png
  32. 15 0
      hichina-main-front-mobile-first-v2/src/assets/quasar-logo-vertical.svg
  33. 0 0
      hichina-main-front-mobile-first-v2/src/boot/.gitkeep
  34. 23 0
      hichina-main-front-mobile-first-v2/src/boot/axios.js
  35. 85 0
      hichina-main-front-mobile-first-v2/src/boot/globalMixin.js
  36. 16 0
      hichina-main-front-mobile-first-v2/src/boot/i18n.js
  37. 49 0
      hichina-main-front-mobile-first-v2/src/components/EssentialLink.vue
  38. 5 0
      hichina-main-front-mobile-first-v2/src/css/app.scss
  39. 25 0
      hichina-main-front-mobile-first-v2/src/css/quasar.variables.scss
  40. 39 0
      hichina-main-front-mobile-first-v2/src/i18n/en-US/index.js
  41. 11 0
      hichina-main-front-mobile-first-v2/src/i18n/index.js
  42. 37 0
      hichina-main-front-mobile-first-v2/src/i18n/ko-KR/index.js
  43. 37 0
      hichina-main-front-mobile-first-v2/src/i18n/ru-RU/index.js
  44. 39 0
      hichina-main-front-mobile-first-v2/src/i18n/th-TH/index.js
  45. 529 0
      hichina-main-front-mobile-first-v2/src/layouts/MainLayout.vue
  46. 69 0
      hichina-main-front-mobile-first-v2/src/pages/AlipayPage.vue
  47. 245 0
      hichina-main-front-mobile-first-v2/src/pages/BlogCreatePage.vue
  48. 265 0
      hichina-main-front-mobile-first-v2/src/pages/BlogDetailPage.vue
  49. 263 0
      hichina-main-front-mobile-first-v2/src/pages/BlogEditPage.vue
  50. 221 0
      hichina-main-front-mobile-first-v2/src/pages/BlogPage.vue
  51. 474 0
      hichina-main-front-mobile-first-v2/src/pages/BookPage.vue
  52. 111 0
      hichina-main-front-mobile-first-v2/src/pages/ContactPage.vue
  53. 227 0
      hichina-main-front-mobile-first-v2/src/pages/DestinationDetailPage.vue
  54. 155 0
      hichina-main-front-mobile-first-v2/src/pages/DestinationPage.vue
  55. 31 0
      hichina-main-front-mobile-first-v2/src/pages/ErrorNotFound.vue
  56. 52 0
      hichina-main-front-mobile-first-v2/src/pages/FinishpayPage.vue
  57. 149 0
      hichina-main-front-mobile-first-v2/src/pages/GuideIntroPage.vue
  58. 399 0
      hichina-main-front-mobile-first-v2/src/pages/IndexPage.vue
  59. 281 0
      hichina-main-front-mobile-first-v2/src/pages/LoginPage.vue
  60. 201 0
      hichina-main-front-mobile-first-v2/src/pages/MyBlogsPage.vue
  61. 110 0
      hichina-main-front-mobile-first-v2/src/pages/MyOrdersPage.vue
  62. 243 0
      hichina-main-front-mobile-first-v2/src/pages/OrderDetailPage.vue
  63. 257 0
      hichina-main-front-mobile-first-v2/src/pages/PrivacyPage.vue
  64. 872 0
      hichina-main-front-mobile-first-v2/src/pages/ProductDetailPage.vue
  65. 33 0
      hichina-main-front-mobile-first-v2/src/pages/RegSuccessPage.vue
  66. 126 0
      hichina-main-front-mobile-first-v2/src/pages/RegisterPage.vue
  67. 64 0
      hichina-main-front-mobile-first-v2/src/pages/RegvalidationPage.vue
  68. 225 0
      hichina-main-front-mobile-first-v2/src/pages/TravelShopPage.vue
  69. 296 0
      hichina-main-front-mobile-first-v2/src/pages/UserInfoPage.vue
  70. 126 0
      hichina-main-front-mobile-first-v2/src/pages/WechatpayPage.vue
  71. 30 0
      hichina-main-front-mobile-first-v2/src/router/index.js
  72. 197 0
      hichina-main-front-mobile-first-v2/src/router/routes.js
  73. 17 0
      hichina-main-front-mobile-first-v2/src/stores/bookParamStore.js
  74. 20 0
      hichina-main-front-mobile-first-v2/src/stores/index.js
  75. 17 0
      hichina-main-front-mobile-first-v2/src/stores/orderPaymentParamStore.js
  76. 10 0
      hichina-main-front-mobile-first-v2/src/stores/store-flag.d.ts
  77. 3432 0
      hichina-main-front-mobile-first-v2/yarn.lock
  78. 1 1
      hichina-main-front-mobile-first/quasar.config.js
  79. 8 0
      hichina-main-front-mobile-first/src-ssr/middlewares/compression.js
  80. 54 0
      hichina-main-front-mobile-first/src-ssr/middlewares/render.js
  81. 17 0
      hichina-main-front-mobile-first/src-ssr/production-export.js
  82. 10 0
      hichina-main-front-mobile-first/src-ssr/ssr-flag.d.ts

+ 1 - 1
hichina-main-back/src/main/resources/application-dev.properties

@@ -2,7 +2,7 @@ server.port=9052
 
 spring.datasource.url=jdbc:mysql://localhost:3306/unified_hichina?zeroDateTimeBehavior=convertToNull&useUnicode=true&allowMultiQueries=true
 spring.datasource.username=root
-spring.datasource.password=Passw0rd
+spring.datasource.password=maimengPassw0rd
 spring.datasource.tomcat.max-wait=20000
 spring.datasource.tomcat.max-active=50
 spring.datasource.tomcat.max-idle=20

+ 9 - 0
hichina-main-front-mobile-first-v2/.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 7 - 0
hichina-main-front-mobile-first-v2/.eslintignore

@@ -0,0 +1,7 @@
+/dist
+/src-capacitor
+/src-cordova
+/.quasar
+/node_modules
+.eslintrc.js
+/quasar.config.*.temporary.compiled*

+ 66 - 0
hichina-main-front-mobile-first-v2/.eslintrc.cjs

@@ -0,0 +1,66 @@
+module.exports = {
+  // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
+  // This option interrupts the configuration hierarchy at this file
+  // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
+  root: true,
+
+  parserOptions: {
+    ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
+  },
+
+  env: {
+    node: true,
+    browser: true,
+    'vue/setup-compiler-macros': true
+  },
+
+  // Rules order is important, please avoid shuffling them
+  extends: [
+    // Base ESLint recommended rules
+    // 'eslint:recommended',
+
+    // Uncomment any of the lines below to choose desired strictness,
+    // but leave only one uncommented!
+    // See https://eslint.vuejs.org/rules/#available-rules
+    'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
+    // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
+    // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
+
+    // https://github.com/prettier/eslint-config-prettier#installation
+    // usage with Prettier, provided by 'eslint-config-prettier'.
+    'prettier'
+  ],
+
+  plugins: [
+    // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
+    // required to lint *.vue files
+    'vue',
+    
+    // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
+    // Prettier has not been included as plugin to avoid performance impact
+    // add it as an extension for your IDE
+    
+  ],
+
+  globals: {
+    ga: 'readonly', // Google Analytics
+    cordova: 'readonly',
+    __statics: 'readonly',
+    __QUASAR_SSR__: 'readonly',
+    __QUASAR_SSR_SERVER__: 'readonly',
+    __QUASAR_SSR_CLIENT__: 'readonly',
+    __QUASAR_SSR_PWA__: 'readonly',
+    process: 'readonly',
+    Capacitor: 'readonly',
+    chrome: 'readonly'
+  },
+
+  // add your custom rules here
+  rules: {
+    
+    'prefer-promise-reject-errors': 'off',
+
+    // allow debugger during development only
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
+  }
+}

+ 33 - 0
hichina-main-front-mobile-first-v2/.gitignore

@@ -0,0 +1,33 @@
+.DS_Store
+.thumbs.db
+node_modules
+
+# Quasar core related directories
+.quasar
+/dist
+/quasar.config.*.temporary.compiled*
+
+# Cordova related directories and files
+/src-cordova/node_modules
+/src-cordova/platforms
+/src-cordova/plugins
+/src-cordova/www
+
+# Capacitor related directories and files
+/src-capacitor/www
+/src-capacitor/node_modules
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+
+# local .env files
+.env.local*

+ 3 - 0
hichina-main-front-mobile-first-v2/.npmrc

@@ -0,0 +1,3 @@
+# pnpm-related options
+shamefully-hoist=true
+strict-peer-dependencies=false

+ 15 - 0
hichina-main-front-mobile-first-v2/.vscode/extensions.json

@@ -0,0 +1,15 @@
+{
+  "recommendations": [
+    "dbaeumer.vscode-eslint",
+    "esbenp.prettier-vscode",
+    "editorconfig.editorconfig",
+    "vue.volar",
+    "wayou.vscode-todo-highlight"
+  ],
+  "unwantedRecommendations": [
+    "octref.vetur",
+    "hookyqr.beautify",
+    "dbaeumer.jshint",
+    "ms-vscode.vscode-typescript-tslint-plugin"
+  ]
+}

+ 15 - 0
hichina-main-front-mobile-first-v2/.vscode/settings.json

@@ -0,0 +1,15 @@
+{
+  "editor.bracketPairColorization.enabled": true,
+  "editor.guides.bracketPairs": true,
+  "editor.formatOnSave": true,
+  "editor.defaultFormatter": "esbenp.prettier-vscode",
+  "editor.codeActionsOnSave": [
+    "source.fixAll.eslint"
+  ],
+  "eslint.validate": [
+    "javascript",
+    "javascriptreact",
+    "typescript",
+    "vue"
+  ]
+}

+ 41 - 0
hichina-main-front-mobile-first-v2/README.md

@@ -0,0 +1,41 @@
+# HiChinaTravel Main Site (hichina-main-front-mobile-first-v2)
+
+A Quasar Project
+
+## Install the dependencies
+```bash
+yarn
+# or
+npm install
+```
+
+### Start the app in development mode (hot-code reloading, error reporting, etc.)
+```bash
+quasar dev
+```
+
+
+### Lint the files
+```bash
+yarn lint
+# or
+npm run lint
+```
+
+
+### Format the files
+```bash
+yarn format
+# or
+npm run format
+```
+
+
+
+### Build the app for production
+```bash
+quasar build
+```
+
+### Customize the configuration
+See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).

+ 21 - 0
hichina-main-front-mobile-first-v2/index.html

@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title><%= productName %></title>
+
+    <meta charset="utf-8">
+    <meta name="description" content="<%= productDescription %>">
+    <meta name="format-detection" content="telephone=no">
+    <meta name="msapplication-tap-highlight" content="no">
+    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
+
+    <link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
+    <link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
+    <link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
+    <link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
+    <link rel="icon" type="image/ico" href="favicon.ico">
+  </head>
+  <body>
+    <!-- quasar:entry-point -->
+  </body>
+</html>

+ 39 - 0
hichina-main-front-mobile-first-v2/jsconfig.json

@@ -0,0 +1,39 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "src/*": [
+        "src/*"
+      ],
+      "app/*": [
+        "*"
+      ],
+      "components/*": [
+        "src/components/*"
+      ],
+      "layouts/*": [
+        "src/layouts/*"
+      ],
+      "pages/*": [
+        "src/pages/*"
+      ],
+      "assets/*": [
+        "src/assets/*"
+      ],
+      "boot/*": [
+        "src/boot/*"
+      ],
+      "stores/*": [
+        "src/stores/*"
+      ],
+      "vue$": [
+        "node_modules/vue/dist/vue.runtime.esm-bundler.js"
+      ]
+    }
+  },
+  "exclude": [
+    "dist",
+    ".quasar",
+    "node_modules"
+  ]
+}

+ 49 - 0
hichina-main-front-mobile-first-v2/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "hichina-main-front-mobile-first-v2",
+  "version": "0.0.1",
+  "description": "A Quasar Project",
+  "productName": "HiChinaTravel Main Site",
+  "author": "fenngchang",
+  "private": true,
+  "scripts": {
+    "lint": "eslint --ext .js,.vue ./",
+    "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
+    "test": "echo \"No test specified\" && exit 0",
+    "dev": "quasar dev",
+    "build": "quasar build"
+  },
+  "devDependencies": {
+    "@babel/eslint-parser": "^7.13.14",
+    "eslint": "^8.10.0",
+    "eslint-plugin-vue": "^9.0.0",
+    "eslint-config-prettier": "^8.1.0",
+    "prettier": "^2.5.1",
+    "@quasar/app-vite": "^1.3.0",
+    "autoprefixer": "^10.4.2",
+    "postcss": "^8.4.14",
+    "@intlify/vue-i18n-loader": "^4.2.0"
+  },
+  "dependencies": {
+    "@quasar/extras": "^1.16.5",
+    "@vueup/vue-quill": "^1.2.0",
+    "axios": "^1.4.0",
+    "core-js": "^3.6.5",
+    "pinia": "^2.1.4",
+    "qrcode-vue3": "^1.6.8",
+    "quasar": "^2.6.0",
+    "quill-image-uploader": "^1.3.0",
+    "unhead": "^1.1.30",
+    "vue": "^3.0.0",
+    "vue-i18n": "^9.3.0-beta.24",
+    "vue-image-crop-upload": "^3.0.3",
+    "vue-json-pretty": "^2.2.4",
+    "vue-router": "^4.0.0",
+    "vue3-country-region-select": "^1.0.0",
+    "vuejs3-datepicker": "^1.0.19"
+  },
+  "engines": {
+    "node": "^18 || ^16 || ^14.19",
+    "npm": ">= 6.13.4",
+    "yarn": ">= 1.21.1"
+  }
+}

+ 27 - 0
hichina-main-front-mobile-first-v2/postcss.config.cjs

@@ -0,0 +1,27 @@
+/* eslint-disable */
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  plugins: [
+    // https://github.com/postcss/autoprefixer
+    require('autoprefixer')({
+      overrideBrowserslist: [
+        'last 4 Chrome versions',
+        'last 4 Firefox versions',
+        'last 4 Edge versions',
+        'last 4 Safari versions',
+        'last 4 Android versions',
+        'last 4 ChromeAndroid versions',
+        'last 4 FirefoxAndroid versions',
+        'last 4 iOS versions'
+      ]
+    })
+
+    // https://github.com/elchininet/postcss-rtlcss
+    // If you want to support RTL css, then
+    // 1. yarn/npm install postcss-rtlcss
+    // 2. optionally set quasar.config.js > framework > lang to an RTL language
+    // 3. uncomment the following line:
+    // require('postcss-rtlcss')
+  ]
+}

BIN
hichina-main-front-mobile-first-v2/public/favicon-old.ico


BIN
hichina-main-front-mobile-first-v2/public/favicon.ico


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-128x128-old.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-128x128.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-16x16-old.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-16x16.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-32x32-old.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-32x32.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-96x96-old.png


BIN
hichina-main-front-mobile-first-v2/public/icons/favicon-96x96.png


BIN
hichina-main-front-mobile-first-v2/public/icons/logo.png


+ 213 - 0
hichina-main-front-mobile-first-v2/quasar.config.js

@@ -0,0 +1,213 @@
+/* eslint-env node */
+
+/*
+ * This file runs in a Node context (it's NOT transpiled by Babel), so use only
+ * the ES6 features that are supported by your Node version. https://node.green/
+ */
+
+// Configuration for your app
+// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
+
+
+const { configure } = require('quasar/wrappers');
+
+
+module.exports = configure(function (/* ctx */) {
+  return {
+    eslint: {
+      // fix: true,
+      // include: [],
+      // exclude: [],
+      // rawOptions: {},
+      warnings: true,
+      errors: true
+    },
+
+    // https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
+    // preFetch: true,
+
+    // app boot file (/src/boot)
+    // --> boot files are part of "main.js"
+    // https://v2.quasar.dev/quasar-cli-vite/boot-files
+    boot: [
+      "i18n", "axios", "globalMixin"
+    ],
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
+    css: [
+      'app.scss'
+    ],
+
+    // https://github.com/quasarframework/quasar/tree/dev/extras
+    extras: [
+      // 'ionicons-v4',
+      // 'mdi-v5',
+      // 'fontawesome-v6',
+      // 'eva-icons',
+      // 'themify',
+      // 'line-awesome',
+      // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
+
+      'roboto-font', // optional, you are not bound to it
+      'material-icons', // optional, you are not bound to it
+    ],
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
+    build: {
+      target: {
+        browser: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
+        node: 'node16'
+      },
+
+      vueRouterMode: 'history', // available values: 'hash', 'history'
+      // vueRouterBase,
+      // vueDevtools,
+      // vueOptionsAPI: false,
+
+      // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
+
+      // publicPath: '/',
+      // analyze: true,
+      // env: {},
+      // rawDefine: {}
+      // ignorePublicFolder: true,
+      // minify: false,
+      // polyfillModulePreload: true,
+      // distDir
+
+      // extendViteConf (viteConf) {},
+      // viteVuePluginOptions: {},
+
+
+      // vitePlugins: [
+      //   [ 'package-name', { ..options.. } ]
+      // ]
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
+    devServer: {
+      // https: true
+      open: true, // opens browser window automatically
+      port: 9583,
+    },
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
+    framework: {
+      config: {},
+
+      // iconSet: 'material-icons', // Quasar icon set
+      // lang: 'en-US', // Quasar language pack
+
+      // For special cases outside of where the auto-import strategy can have an impact
+      // (like functional components as one of the examples),
+      // you can manually specify Quasar components/directives to be available everywhere:
+      //
+      // components: [],
+      // directives: [],
+
+      // Quasar plugins
+      plugins: ["Notify", "Loading"]
+    },
+
+    // animations: 'all', // --- includes all animations
+    // https://v2.quasar.dev/options/animations
+    animations: [],
+
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
+    // sourceFiles: {
+    //   rootComponent: 'src/App.vue',
+    //   router: 'src/router/index',
+    //   store: 'src/store/index',
+    //   registerServiceWorker: 'src-pwa/register-service-worker',
+    //   serviceWorker: 'src-pwa/custom-service-worker',
+    //   pwaManifestFile: 'src-pwa/manifest.json',
+    //   electronMain: 'src-electron/electron-main',
+    //   electronPreload: 'src-electron/electron-preload'
+    // },
+
+    // https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
+    ssr: {
+      // ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
+                                          // will mess up SSR
+
+      // extendSSRWebserverConf (esbuildConf) {},
+      // extendPackageJson (json) {},
+
+      pwa: false,
+
+      // manualStoreHydration: true,
+      // manualPostHydrationTrigger: true,
+
+      prodPort: 9583, // The default port that the production server should use
+                      // (gets superseded if process.env.PORT is specified at runtime)
+
+      middlewares: [
+        'render' // keep this as last one
+      ]
+    },
+
+    // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
+    pwa: {
+      workboxMode: 'generateSW', // or 'injectManifest'
+      injectPwaMetaTags: true,
+      swFilename: 'sw.js',
+      manifestFilename: 'manifest.json',
+      useCredentialsForManifestTag: false,
+      // useFilenameHashes: true,
+      // extendGenerateSWOptions (cfg) {}
+      // extendInjectManifestOptions (cfg) {},
+      // extendManifestJson (json) {}
+      // extendPWACustomSWConf (esbuildConf) {}
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
+    cordova: {
+      // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
+    capacitor: {
+      hideSplashscreen: true
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
+    electron: {
+      // extendElectronMainConf (esbuildConf)
+      // extendElectronPreloadConf (esbuildConf)
+
+      // specify the debugging port to use for the Electron app when running in development mode
+      inspectPort: 5858,
+
+      bundler: 'packager', // 'packager' or 'builder'
+
+      packager: {
+        // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
+
+        // OS X / Mac App Store
+        // appBundleId: '',
+        // appCategoryType: '',
+        // osxSign: '',
+        // protocol: 'myapp://path',
+
+        // Windows only
+        // win32metadata: { ... }
+      },
+
+      builder: {
+        // https://www.electron.build/configuration/configuration
+
+        appId: 'hichina-main-front-mobile-first-v2'
+      }
+    },
+
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
+    bex: {
+      contentScripts: [
+        'my-content-script'
+      ],
+
+      // extendBexScriptsConf (esbuildConf) {}
+      // extendBexManifestJson (json) {}
+    }
+  }
+});

+ 58 - 0
hichina-main-front-mobile-first-v2/src-ssr/middlewares/render.js

@@ -0,0 +1,58 @@
+import { ssrMiddleware } from 'quasar/wrappers'
+
+// This middleware should execute as last one
+// since it captures everything and tries to
+// render the page with Vue
+
+export default ssrMiddleware(({ app, resolve, render, serve }) => {
+  // we capture any other Express route and hand it
+  // over to Vue and Vue Router to render our page
+  app.get(resolve.urlPath('*'), (req, res) => {
+    res.setHeader('Content-Type', 'text/html')
+
+    render(/* the ssrContext: */ { req, res })
+      .then(html => {
+        // now let's send the rendered html to the client
+        res.send(html)
+      })
+      .catch(err => {
+        // oops, we had an error while rendering the page
+
+        // we were told to redirect to another URL
+        if (err.url) {
+          if (err.code) {
+            res.redirect(err.code, err.url)
+          } else {
+            res.redirect(err.url)
+          }
+        } else if (err.code === 404) {
+          // hmm, Vue Router could not find the requested route
+
+          // Should reach here only if no "catch-all" route
+          // is defined in /src/routes
+          res.status(404).send('404 | Page Not Found')
+        } else if (process.env.DEV) {
+          // well, we treat any other code as error;
+          // if we're in dev mode, then we can use Quasar CLI
+          // to display a nice error page that contains the stack
+          // and other useful information
+
+          // serve.error is available on dev only
+          serve.error({ err, req, res })
+        } else {
+          // we're in production, so we should have another method
+          // to display something to the client when we encounter an error
+          // (for security reasons, it's not ok to display the same wealth
+          // of information as we do in development)
+
+          // Render Error Page on production or
+          // create a route (/src/routes) for an error page and redirect to it
+          res.status(500).send('500 | Internal Server Error')
+
+          if (process.env.DEBUGGING) {
+            console.error(err.stack)
+          }
+        }
+      })
+  })
+})

+ 136 - 0
hichina-main-front-mobile-first-v2/src-ssr/server.js

@@ -0,0 +1,136 @@
+/**
+ * More info about this file:
+ * https://v2.quasar.dev/quasar-cli-vite/developing-ssr/ssr-webserver
+ *
+ * Runs in Node context.
+ */
+
+/**
+ * Make sure to yarn add / npm install (in your project root)
+ * anything you import here (except for express and compression).
+ */
+import express from 'express'
+import compression from 'compression'
+import {
+  ssrClose,
+  ssrCreate,
+  ssrListen,
+  ssrRenderPreloadTag,
+  ssrServeStaticContent
+} from 'quasar/wrappers'
+
+/**
+ * Create your webserver and return its instance.
+ * If needed, prepare your webserver to receive
+ * connect-like middlewares.
+ *
+ * Should NOT be async!
+ */
+export const create = ssrCreate((/* { ... } */) => {
+  const app = express()
+
+  // attackers can use this header to detect apps running Express
+  // and then launch specifically-targeted attacks
+  app.disable('x-powered-by')
+
+  // place here any middlewares that
+  // absolutely need to run before anything else
+  if (process.env.PROD) {
+    app.use(compression())
+  }
+
+  return app
+})
+
+/**
+ * You need to make the server listen to the indicated port
+ * and return the listening instance or whatever you need to
+ * close the server with.
+ *
+ * The "listenResult" param for the "close()" definition below
+ * is what you return here.
+ *
+ * For production, you can instead export your
+ * handler for serverless use or whatever else fits your needs.
+ */
+export const listen = ssrListen(async ({ app, port, isReady }) => {
+  await isReady()
+  return app.listen(port, () => {
+    if (process.env.PROD) {
+      console.log('Server listening at port ' + port)
+    }
+  })
+})
+
+/**
+ * Should close the server and free up any resources.
+ * Will be used on development only when the server needs
+ * to be rebooted.
+ *
+ * Should you need the result of the "listen()" call above,
+ * you can use the "listenResult" param.
+ *
+ * Can be async.
+ */
+export const close = ssrClose(({ listenResult }) => {
+  return listenResult.close()
+})
+
+const maxAge = process.env.DEV
+  ? 0
+  : 1000 * 60 * 60 * 24 * 30
+
+/**
+ * Should return middleware that serves the indicated path
+ * with static content.
+ */
+export const serveStaticContent = ssrServeStaticContent((path, opts) => {
+  return express.static(path, {
+    maxAge,
+    ...opts
+  })
+})
+
+const jsRE = /\.js$/
+const cssRE = /\.css$/
+const woffRE = /\.woff$/
+const woff2RE = /\.woff2$/
+const gifRE = /\.gif$/
+const jpgRE = /\.jpe?g$/
+const pngRE = /\.png$/
+
+/**
+ * Should return a String with HTML output
+ * (if any) for preloading indicated file
+ */
+export const renderPreloadTag = ssrRenderPreloadTag((file) => {
+  if (jsRE.test(file) === true) {
+    return `<link rel="modulepreload" href="${file}" crossorigin>`
+  }
+
+  if (cssRE.test(file) === true) {
+    return `<link rel="stylesheet" href="${file}">`
+  }
+
+  if (woffRE.test(file) === true) {
+    return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
+  }
+
+  if (woff2RE.test(file) === true) {
+    return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
+  }
+
+  if (gifRE.test(file) === true) {
+    return `<link rel="preload" href="${file}" as="image" type="image/gif">`
+  }
+
+  if (jpgRE.test(file) === true) {
+    return `<link rel="preload" href="${file}" as="image" type="image/jpeg">`
+  }
+
+  if (pngRE.test(file) === true) {
+    return `<link rel="preload" href="${file}" as="image" type="image/png">`
+  }
+
+  return ''
+})

+ 10 - 0
hichina-main-front-mobile-first-v2/src-ssr/ssr-flag.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
+//  REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
+import "quasar/dist/types/feature-flag";
+
+declare module "quasar/dist/types/feature-flag" {
+  interface QuasarFeatureFlags {
+    ssr: true;
+  }
+}

+ 11 - 0
hichina-main-front-mobile-first-v2/src/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <router-view />
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'App'
+})
+</script>

BIN
hichina-main-front-mobile-first-v2/src/assets/hichinalogo.png


BIN
hichina-main-front-mobile-first-v2/src/assets/profileplaceholder.png


+ 15 - 0
hichina-main-front-mobile-first-v2/src/assets/quasar-logo-vertical.svg

@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
+	<path
+		d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
+	<path fill="#050A14"
+		d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
+	<path fill="#00B4FF"
+		d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
+	<path fill="#00B4FF"
+		d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
+	<path fill="#050A14"
+		d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
+	<path fill="#00B4FF"
+		d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
+	<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
+</svg>

+ 0 - 0
hichina-main-front-mobile-first-v2/src/boot/.gitkeep


+ 23 - 0
hichina-main-front-mobile-first-v2/src/boot/axios.js

@@ -0,0 +1,23 @@
+// src/boot/axios.js
+
+import { boot } from "quasar/wrappers";
+import axios from "axios";
+
+const api = axios.create({
+  baseURL: "http://localhost:9052",
+  withCredentials: true,
+});
+
+export default boot(({ app }) => {
+  // for use inside Vue files (Options API) through this.$axios and this.$api
+
+  app.config.globalProperties.$axios = axios;
+  // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
+  //       so you won't necessarily have to import axios in each vue file
+
+  app.config.globalProperties.$api = api;
+  // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
+  //       so you can easily perform requests against your app's API
+});
+
+export { axios, api };

+ 85 - 0
hichina-main-front-mobile-first-v2/src/boot/globalMixin.js

@@ -0,0 +1,85 @@
+import { boot } from "quasar/wrappers";
+import vueCountryRegionSelect from "vue3-country-region-select";
+
+// "async" is optional;
+// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
+export default boot(async (/* { app, router, ... } */ { app, router }) => {
+  // something to do
+  const testGlobal2 = () => {
+    console.log("testGlobal2");
+  };
+
+  app.use(vueCountryRegionSelect);
+
+  const normalizeMultiImageUrl = (input) => {
+    if (input.indexOf(",") > -1) {
+      return input.split(",").shift();
+    } else if (input.indexOf(";") > -1) {
+      return input.split(";").shift();
+    }
+    return input;
+  };
+
+  const removeHtmlTag = (input) => {
+    var div = document.createElement("div");
+    div.innerHTML = input;
+    var text = div.textContent || div.innerText || "";
+    return text;
+  };
+
+  const generalNotify = (q, isPositive, message) => {
+    q.notify({
+      position: "top-right",
+      timeout: 2500,
+      color: isPositive ? "positive" : "negative",
+      textColor: "white",
+      message: message,
+      actions: [{ icon: "close", color: "white" }],
+    });
+  };
+
+  const goPage = (val) => {
+    router.push(val);
+  };
+
+  const checkEmpty = (val) => {
+    if (val == null || val.length < 1) {
+      return true;
+    } else {
+      return false;
+    }
+  };
+
+  const showLoading = (q) => {
+    q.loading.show();
+  };
+
+  const hideLoading = (q) => {
+    q.loading.hide();
+  };
+
+  app.config.globalProperties.$testGlobal2 = testGlobal2;
+  app.config.globalProperties.$normalizeMultiImageUrl = normalizeMultiImageUrl;
+  app.config.globalProperties.$removeHtmlTag = removeHtmlTag;
+  app.config.globalProperties.$generalNotify = generalNotify;
+  app.config.globalProperties.$goPage = goPage;
+  app.config.globalProperties.$checkEmpty = checkEmpty;
+  app.config.globalProperties.$showLoading = showLoading;
+  app.config.globalProperties.$hideLoading = hideLoading;
+
+  app.mixin({
+    methods: {
+      testGlobal2,
+      generalNotify,
+      normalizeMultiImageUrl,
+      removeHtmlTag,
+      gotoUrl(url) {
+        window.location = url;
+      },
+      goPage,
+      checkEmpty,
+      showLoading,
+      hideLoading,
+    },
+  });
+});

+ 16 - 0
hichina-main-front-mobile-first-v2/src/boot/i18n.js

@@ -0,0 +1,16 @@
+import { createI18n } from "vue-i18n";
+import messages from "src/i18n";
+import { boot } from "quasar/wrappers";
+
+export default boot(({ app }) => {
+  // Create I18n instance
+  const i18n = createI18n({
+    locale: "en-US",
+    globalInjection: true,
+    legacy: false,
+    messages,
+  });
+
+  // Tell app to use the I18n instance
+  app.use(i18n);
+});

+ 49 - 0
hichina-main-front-mobile-first-v2/src/components/EssentialLink.vue

@@ -0,0 +1,49 @@
+<template>
+  <q-item
+    clickable
+    tag="a"
+    target="_blank"
+    :href="link"
+  >
+    <q-item-section
+      v-if="icon"
+      avatar
+    >
+      <q-icon :name="icon" />
+    </q-item-section>
+
+    <q-item-section>
+      <q-item-label>{{ title }}</q-item-label>
+      <q-item-label caption>{{ caption }}</q-item-label>
+    </q-item-section>
+  </q-item>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'EssentialLink',
+  props: {
+    title: {
+      type: String,
+      required: true
+    },
+
+    caption: {
+      type: String,
+      default: ''
+    },
+
+    link: {
+      type: String,
+      default: '#'
+    },
+
+    icon: {
+      type: String,
+      default: ''
+    }
+  }
+})
+</script>

+ 5 - 0
hichina-main-front-mobile-first-v2/src/css/app.scss

@@ -0,0 +1,5 @@
+// app global css in SCSS form
+img {
+  max-width: 100%;
+  height: auto;
+}

+ 25 - 0
hichina-main-front-mobile-first-v2/src/css/quasar.variables.scss

@@ -0,0 +1,25 @@
+// Quasar SCSS (& Sass) Variables
+// --------------------------------------------------
+// To customize the look and feel of this app, you can override
+// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
+
+// Check documentation for full list of Quasar variables
+
+// Your own variables (that are declared here) and Quasar's own
+// ones will be available out of the box in your .vue/.scss/.sass files
+
+// It's highly recommended to change the default colors
+// to match your app's branding.
+// Tip: Use the "Theme Builder" on Quasar's documentation website.
+
+$primary   : #1976D2;
+$secondary : #26A69A;
+$accent    : #9C27B0;
+
+$dark      : #1D1D1D;
+$dark-page : #121212;
+
+$positive  : #21BA45;
+$negative  : #C10015;
+$info      : #31CCEC;
+$warning   : #F2C037;

+ 39 - 0
hichina-main-front-mobile-first-v2/src/i18n/en-US/index.js

@@ -0,0 +1,39 @@
+// This is just an example,
+// so you can safely delete all default props below
+
+export default {
+  register: "Register",
+  login: "Login",
+  logout: "Logout",
+  edit_profile: "Edit Profile",
+  my_orders: "My Orders",
+  my_blogs: "My Blogs",
+  language: "Language",
+  guidebooks: "GuideBooks",
+  blogs_vlogs: "Blogs/Vlogs",
+  travel_shop: "Travel Shop",
+  destinations: "Destinations",
+  more_destinations: "More Destinations",
+  places_in_china: "Places to go in China",
+  tailor_made_trip: "Want a Tailor Made Trip to China? Click Here for Help",
+  people_are_traveling: "People are traveling",
+  whats_in_china: "What's in China",
+  latest_guidebooks: "Latest Guidebooks",
+  most_downloaded_guidebooks: "Most Downloaded Guidebooks",
+  blogs_of_the_week: "Blogs of The Week",
+  write_your_blog: "Write your blog",
+  latest_blogs: "Latest blogs",
+  most_viewed_in_month: "Most viewed in a month",
+  blogers_m_following: "Bloggers I'm following",
+  group_tour: "Group tour",
+  hotel_deals: "Hotel deals",
+  flight_deals: "Flight deals",
+  holiday_package: "Holiday package",
+  china_stuff: "China stuff",
+  search_by_title: "Search by title",
+  deals_for_you: "Deals for you",
+  seasonal_recommendation: "Seasonal Recommendation",
+  enter_email: "Enter your email",
+  enter_password: "Enter your password",
+  confirm_password: "Confirm your password",
+};

+ 11 - 0
hichina-main-front-mobile-first-v2/src/i18n/index.js

@@ -0,0 +1,11 @@
+import enUS from "./en-US";
+import thTH from "./th-TH";
+import koKR from "./ko-KR";
+import ruRU from "./ru-RU";
+
+export default {
+  "en-US": enUS,
+  "th-TH": thTH,
+  "ko-KR": koKR,
+  "ru-RU": ruRU,
+};

+ 37 - 0
hichina-main-front-mobile-first-v2/src/i18n/ko-KR/index.js

@@ -0,0 +1,37 @@
+// This is just an example,
+// so you can safely delete all default props below
+
+export default {
+  register: "등록하다",
+  login: "로그인",
+  logout: "로그 아웃",
+  edit_profile: "프로필 수정",
+  my_orders: "내 주문",
+  my_blogs: "내 블로그",
+  language: "언어",
+  guidebooks: "가이드북",
+  blogs_vlogs: "블로그/브이로그",
+  travel_shop: "트래블 샵",
+  destinations: "목적지",
+  more_destinations: "더 많은 목적지",
+  places_in_china: "중국에서 갈 장소",
+  tailor_made_trip:
+    "맞춤형 중국 여행을 원하십니까? 도움말을 보려면 여기를 클릭하세요.",
+  people_are_traveling: "사람들이 여행하고 있다",
+  whats_in_china: "중국에 있는 것",
+  latest_guidebooks: "최신 가이드북",
+  most_downloaded_guidebooks: "가장 많이 다운로드된 가이드북",
+  blogs_of_the_week: "금주의 블로그",
+  write_your_blog: "블로그 작성",
+  latest_blogs: "최신 블로그",
+  most_viewed_in_month: "한 달 동안 가장 많이 본",
+  blogers_m_following: "내가 팔로우하는 블로거",
+  group_tour: "단체관광",
+  hotel_deals: "호텔 상품",
+  flight_deals: "항공편 특가",
+  holiday_package: "홀리데이 패키지",
+  china_stuff: "중국 물건",
+  search_by_title: "제목으로 검색",
+  deals_for_you: "당신을 위한 거래",
+  seasonal_recommendation: "계절 추천",
+};

+ 37 - 0
hichina-main-front-mobile-first-v2/src/i18n/ru-RU/index.js

@@ -0,0 +1,37 @@
+// This is just an example,
+// so you can safely delete all default props below
+
+export default {
+  register: "регистр",
+  login: "Авторизоваться",
+  logout: "Выйти",
+  edit_profile: "Редактировать профиль",
+  my_orders: "мои заказы",
+  my_blogs: "Мои блоги",
+  language: "Язык",
+  guidebooks: "Путеводители",
+  blogs_vlogs: "Блоги/Влоги",
+  travel_shop: "Туристический магазин",
+  destinations: "Направления",
+  more_destinations: "Больше направлений",
+  places_in_china: "Куда сходить в Китае",
+  tailor_made_trip:
+    "Хотите индивидуальное путешествие в Китай? Нажмите здесь, чтобы получить помощь",
+  people_are_traveling: "Люди путешествуют",
+  whats_in_china: "Что в Китае",
+  latest_guidebooks: "Последние путеводители",
+  most_downloaded_guidebooks: "Самые скачиваемые путеводители",
+  blogs_of_the_week: "Блоги недели",
+  write_your_blog: "Напишите свой блог",
+  latest_blogs: "Последние блоги",
+  most_viewed_in_month: "Самые просматриваемые за месяц",
+  blogers_m_following: "Блоггеры, на которых я подписан",
+  group_tour: "Групповой тур",
+  hotel_deals: "Предложения от отелей",
+  flight_deals: "Авиабилеты",
+  holiday_package: "Праздничный пакет",
+  china_stuff: "Китайские вещи",
+  search_by_title: "Поиск по названию",
+  deals_for_you: "Предложения для вас",
+  seasonal_recommendation: "Seasonal Recommendation",
+};

+ 39 - 0
hichina-main-front-mobile-first-v2/src/i18n/th-TH/index.js

@@ -0,0 +1,39 @@
+// This is just an example,
+// so you can safely delete all default props below
+
+export default {
+  register: "ลงทะเบียน",
+  login: "เข้าสู่ระบบ",
+  logout: "ออกจากระบบ",
+  edit_profile: "แก้ไขโปรไฟล์",
+  my_orders: "คำสั่งซื้อของฉัน",
+  my_blogs: "บล็อกของฉัน",
+  language: "ภาษา",
+  guidebooks: "คู่มือท่องเที่ยว",
+  blogs_vlogs: "บล็อกท่องเที่ยว",
+  travel_shop: "ร้านค้า",
+  destinations: "จุดหมายปลายทาง",
+  more_destinations: "จุดหมายปลายทางเพิ่มเติม",
+  places_in_china: "สถานที่น่าไปในจีน",
+  tailor_made_trip: "ต้องการแพลนทริปไปประเทศจีน คลิกที่นี่",
+  people_are_traveling: "ผู้คนกำลังสนใจ",
+  whats_in_china: "สถานที่ในประเทศจีน",
+  latest_guidebooks: "คู่มือล่าสุด",
+  most_downloaded_guidebooks: "คู่มือแนะนำที่ยอดชมมากที่สุด",
+  blogs_of_the_week: "บล็อกประจำสัปดาห์",
+  write_your_blog: "เขียนบล็อกของคุณ",
+  latest_blogs: "บล็อกล่าสุด",
+  most_viewed_in_month: "เข้าชมมากที่สุด",
+  blogers_m_following: "บล็อกเกอร์ที่ฉันติดตาม",
+  group_tour: "กรุ๊ปทัวร์",
+  hotel_deals: "โรงแรม",
+  flight_deals: "เที่ยวบิน",
+  holiday_package: "แพ็คเกจวันหยุด",
+  china_stuff: "สินค้า",
+  search_by_title: "ค้นหาตามชื่อเรื่อง",
+  deals_for_you: "ข้อเสนอสำหรับคุณ",
+  seasonal_recommendation: "คำแนะนำตามฤดูกาล",
+  enter_email: "กรุณากรอกอีเมลของคุณ",
+  enter_password: "กรุณากรอกรหัสผ่านของคุณ",
+  confirm_password: "กรุณายืนยันรหัสผ่านของคุณ",
+};

+ 529 - 0
hichina-main-front-mobile-first-v2/src/layouts/MainLayout.vue

@@ -0,0 +1,529 @@
+<template>
+  <q-layout view="hHh lpR fff">
+    <q-header height-hint="76" reveal class="bg-white text-black">
+      <q-toolbar class="GPL__toolbar" style="height: 76px">
+        <div class="row">
+          <div class="col-sm-12 col-xs-12 q-ml-xl q-mr-lg">
+            <q-toolbar-title>
+              <img
+                @click="$router.push('/')"
+                class="cursor-pointer float-left"
+                src="~/assets/hichinalogo.png"
+              />
+            </q-toolbar-title>
+          </div>
+        </div>
+        <q-btn
+          @click="goPage('/guideintro')"
+          flat
+          no-caps
+          no-wrap
+          class="q-ml-xl"
+          v-if="$q.screen.gt.xs"
+        >
+          {{ $t("guidebooks") }}
+        </q-btn>
+        <q-btn
+          @click="goPage('/blog')"
+          flat
+          no-caps
+          no-wrap
+          class="q-ml-sm"
+          v-if="$q.screen.gt.xs"
+        >
+          {{ $t("blogs_vlogs") }}
+        </q-btn>
+        <q-btn
+          @click="goPage('/product')"
+          flat
+          no-caps
+          no-wrap
+          class="q-ml-sm"
+          v-if="$q.screen.gt.xs"
+        >
+          {{ $t("travel_shop") }}
+        </q-btn>
+        <q-btn
+          @click="goPage('/destination')"
+          flat
+          no-caps
+          no-wrap
+          class="q-ml-sm"
+          v-if="$q.screen.gt.xs"
+        >
+          {{ $t("destinations") }}
+        </q-btn>
+
+        <!-- this is the trick -->
+        <q-space />
+
+        <div class="row no-wrap q-mr-md">
+          <q-select
+            v-model="locale"
+            :options="localeOptions"
+            :label="$t('language')"
+            dense
+            emit-value
+            map-options
+            options-dense
+            style="min-width: 150px"
+            @update:model-value="setLanguage"
+            ><template v-slot:append> <q-icon name="public" /> </template
+          ></q-select>
+        </div>
+
+        <div v-if="currentUser === ''" class="row no-wrap">
+          <q-btn
+            @click="goPage('/auth/register')"
+            flat
+            dense
+            no-wrap
+            no-caps
+            color="primary"
+            :label="$t('register')"
+            class="q-mr-md"
+            v-if="$q.screen.gt.xs"
+          />
+          <q-btn
+            @click="goPage('/auth/login')"
+            flat
+            dense
+            no-wrap
+            no-caps
+            color="primary"
+            :label="$t('login')"
+            class="q-mr-sm"
+            v-if="$q.screen.gt.xs"
+          />
+        </div>
+        <div v-if="currentUser != ''" class="row">
+          <q-btn
+            v-if="$q.screen.gt.xs"
+            round
+            flat
+            @click="goPage('/user-info')"
+          >
+            <q-avatar size="36px">
+              <img :src="currentProfileImage" />
+            </q-avatar>
+            <q-tooltip>{{ currentUser }}</q-tooltip>
+          </q-btn>
+        </div>
+        <div v-if="currentUser != '' && $q.screen.gt.xs" class="row q-mr-xl">
+          <q-btn icon="arrow_drop_down" flat dense>
+            <q-menu
+              style="width: 100px"
+              transition-show="flip-right"
+              transition-hide="flip-left"
+            >
+              <q-list dense class="text-grey-9 text-caption">
+                <q-item clickable @click="goPage('/my-blogs')">
+                  <q-item-section>{{ $t("my_blogs") }}</q-item-section>
+                </q-item>
+                <q-item clickable @click="goPage('/my-orders')">
+                  <q-item-section>{{ $t("my_orders") }}</q-item-section>
+                </q-item>
+                <q-item clickable @click="goPage('/user-info')">
+                  <q-item-section>{{ $t("edit_profile") }}</q-item-section>
+                </q-item>
+                <q-separator />
+                <q-item clickable @click="logout()">
+                  <q-item-section>{{ $t("logout") }}</q-item-section>
+                </q-item>
+              </q-list>
+            </q-menu>
+          </q-btn>
+        </div>
+        <!-- <div v-if="currentUser != ''" class="row no-wrap q-ml-md">
+          <q-btn
+            @click="logout()"
+            v-if="$q.screen.gt.xs"
+            round
+            dense
+            color="primary"
+            class="q-mr-md"
+            icon="logout"
+            ><q-tooltip>logout</q-tooltip></q-btn
+          >
+        </div> -->
+        <div>
+          <q-btn
+            v-if="!$q.screen.gt.xs"
+            flat
+            dense
+            round
+            @click="toggleLeftDrawer"
+            icon="menu"
+            aria-label="Menu"
+          />
+        </div>
+      </q-toolbar>
+    </q-header>
+
+    <q-drawer v-model="leftDrawerOpen" bordered class="bg-grey-2" :width="240">
+      <q-scroll-area class="fit">
+        <q-list padding>
+          <q-item v-ripple clickable @click="goPage('/guideintro')">
+            <q-item-section avatar>
+              <q-icon color="grey" name="fingerprint" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("guidebooks") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item v-ripple clickable @click="goPage('/blog')">
+            <q-item-section avatar>
+              <q-icon color="grey" name="fingerprint" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("blogs_vlogs") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item v-ripple clickable @click="goPage('/product')">
+            <q-item-section avatar>
+              <q-icon color="grey" name="fingerprint" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("travel_shop") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item v-ripple clickable @click="goPage('/destination')">
+            <q-item-section avatar>
+              <q-icon color="grey" name="fingerprint" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("destinations") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+
+          <q-separator class="q-my-md" />
+
+          <q-item
+            v-if="currentUser === ''"
+            v-ripple
+            clickable
+            @click="goPage('/auth/login')"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="login" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("login") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item
+            v-if="currentUser === ''"
+            v-ripple
+            clickable
+            @click="goPage('/auth/register')"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="account_circle" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("register") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item
+            v-if="currentUser !== ''"
+            v-ripple
+            clickable
+            @click="goPage('/my-blogs')"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="rss_feed" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("my_blogs") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item
+            v-if="currentUser !== ''"
+            v-ripple
+            clickable
+            @click="goPage('/my-orders')"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="list_alt" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("my_orders") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item
+            v-if="currentUser !== ''"
+            v-ripple
+            clickable
+            @click="goPage('/user-info')"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="manage_accounts" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("edit_profile") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+          <q-item
+            v-if="currentUser !== ''"
+            v-ripple
+            clickable
+            @click="logout()"
+          >
+            <q-item-section avatar>
+              <q-icon color="grey" name="logout" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>{{ $t("logout") }}</q-item-label>
+            </q-item-section>
+          </q-item>
+        </q-list>
+      </q-scroll-area>
+    </q-drawer>
+
+    <q-page-container>
+      <router-view />
+      <q-footer bordered class="bg-blue-6 text-white">
+        <div class="q-pa-md">
+          <div class="row justify-center">
+            <div class="col-12 col-md-3 text-white q-pl-xl q-pt-md">
+              <div class="text-subtitle1 text-weight-bold">
+                About HiChinaTravel
+              </div>
+              <div class="text-body2 hover_underline_white q-mt-md">
+                About Us
+              </div>
+              <div class="text-body2 hover_underline_white q-mt-md">
+                Contact us
+              </div>
+              <div class="text-body2 hover_underline_white q-mt-md">
+                Copyright
+              </div>
+              <div
+                class="text-body2 hover_underline_white q-mt-md cursor-pointer"
+                @click="goPage('/privacy')"
+              >
+                Privacy
+              </div>
+              <div class="text-body2 hover_underline_white q-mt-md">
+                Join Us
+              </div>
+            </div>
+            <div class="col-12 col-md-5 q-pt-xl">
+              <div
+                class="text-subtitle1 text-weight-medium hover_underline_white q-mt-md"
+              >
+                Facebook: HiChinaTravel
+              </div>
+              <div
+                class="text-subtitle1 text-weight-medium hover_underline_white q-mt-md"
+              >
+                WeChat Official Account: HCTravel
+              </div>
+              <div
+                class="text-subtitle1 text-weight-medium hover_underline_white q-mt-md"
+              >
+                Instagram: HiChinaTravel
+              </div>
+              <div
+                class="text-subtitle1 text-weight-medium hover_underline_white q-mt-md"
+              >
+                Support: customerservice@hichinatrip.com
+              </div>
+              <div
+                class="text-subtitle1 text-weight-medium hover_underline_white q-mt-md"
+              >
+                Address: 4,Zhixin Rd. Qixia District Nanjing City, Jiangsu
+                Province, P.R China
+              </div>
+            </div>
+            <div class="col-12 col-md-4"></div>
+          </div>
+          <div class="row justify-center q-mt-xl">
+            <div class="text-body2 text-weight-medium hover_underline_white">
+              COPYRIGHT © 2015-2023 WWW.HICHINATRAVEL.COM, ALL RIGHTS RESERVED
+            </div>
+          </div>
+          <div class="row justify-center">
+            <div class="text-body2 text-weight-medium hover_underline_white">
+              备案号: 京ICP备16006305号-1
+            </div>
+          </div>
+        </div>
+      </q-footer>
+      <!-- Messenger Chat Plugin Code -->
+      <div id="fb-root"></div>
+
+      <!-- Your Chat Plugin code -->
+      <div id="fb-customer-chat" class="fb-customerchat"></div>
+    </q-page-container>
+  </q-layout>
+</template>
+
+<script>
+import {
+  defineComponent,
+  onMounted,
+  onBeforeMount,
+  ref,
+  getCurrentInstance,
+} from "vue";
+import { api } from "boot/axios";
+import { useI18n } from "vue-i18n";
+import { useQuasar } from "quasar";
+
+export default defineComponent({
+  name: "MainLayout",
+
+  components: {},
+
+  setup() {
+    const { locale } = useI18n({ useScope: "global" });
+
+    const leftDrawerOpen = ref(false);
+    const currentUser = ref("");
+    const currentProfileImage = ref("");
+
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    function setLanguage() {
+      // $q.lang.set(locale);
+      console.log(locale);
+    }
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoamiv2")
+        .then(function (response) {
+          console.log("current user:" + response.data.data.username);
+          console.log(
+            "current user profile image:" + response.data.data.profileImageUrl
+          );
+          currentUser.value = response.data.data.username;
+          currentProfileImage.value = response.data.data.profileImageUrl;
+          if (
+            currentProfileImage.value == null ||
+            currentProfileImage.value.length < 1
+          ) {
+            currentProfileImage.value =
+              "https://photoprism.hichinatravel.com/api/v1/t/8623903789c65a160279faa0b33159413cb18af4/32mcf2k4/fit_2048";
+          }
+          console.log("currentProfileImage.value");
+          console.log(currentProfileImage.value);
+        })
+        .catch(function (error) {
+          console.log("not logged in err");
+          // router.push({name: 'home'})
+        });
+    }
+    function logout() {
+      console.log("logging out...");
+      api
+        .post(
+          "/logout",
+          {},
+          { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
+        )
+        .then((response) => {
+          location.reload();
+        })
+        .catch((e) => {
+          location.reload();
+        });
+    }
+    function setupChat() {
+      var chatbox = document.getElementById("fb-customer-chat");
+      chatbox.setAttribute("page_id", "1534271790187013");
+      chatbox.setAttribute("attribution", "biz_inbox");
+    }
+    function initFacebookSDK() {
+      window.fbAsyncInit = function () {
+        FB.init({
+          xfbml: true,
+          version: "v17.0",
+        });
+      };
+
+      (function (d, s, id) {
+        var js,
+          fjs = d.getElementsByTagName(s)[0];
+        if (d.getElementById(id)) return;
+        js = d.createElement(s);
+        js.id = id;
+        js.src = "https://connect.facebook.net/en_US/sdk/xfbml.customerchat.js";
+        fjs.parentNode.insertBefore(js, fjs);
+      })(document, "script", "facebook-jssdk");
+    }
+    onMounted(() => {
+      whoami();
+      setupChat();
+    });
+    onBeforeMount(() => {
+      initFacebookSDK();
+    });
+    return {
+      setLanguage,
+      locale,
+      localeOptions: [
+        { value: "en-US", label: "English" },
+        { value: "th-TH", label: "ภาษาไทย" },
+        { value: "ko-KR", label: "한국인" },
+        { value: "ru-RU", label: "Русский" },
+      ],
+      leftDrawerOpen,
+      currentUser,
+      currentProfileImage,
+      menu_profile: false,
+      logout,
+      toggleLeftDrawer() {
+        leftDrawerOpen.value = !leftDrawerOpen.value;
+      },
+    };
+  },
+});
+</script>
+<style lang="sass">
+.GPL
+
+  &__toolbar
+    height: 64px
+
+  &__toolbar-input
+    width: 35%
+
+  &__drawer-item
+    line-height: 24px
+    border-radius: 0 24px 24px 0
+    margin-right: 12px
+
+    .q-item__section--avatar
+      padding-left: 12px
+      .q-icon
+        color: #5f6368
+
+    .q-item__label:not(.q-item__label--caption)
+      color: #3c4043
+      letter-spacing: .01785714em
+      font-size: .875rem
+      font-weight: 500
+      line-height: 1.25rem
+
+    &--storage
+      border-radius: 0
+      margin-right: 0
+      padding-top: 24px
+      padding-bottom: 24px
+
+  &__side-btn
+    &__label
+      font-size: 12px
+      line-height: 24px
+      letter-spacing: .01785714em
+      font-weight: 500
+
+  @media (min-width: 1024px)
+    &__page-container
+      padding-left: 94px
+</style>

+ 69 - 0
hichina-main-front-mobile-first-v2/src/pages/AlipayPage.vue

@@ -0,0 +1,69 @@
+<template>
+  <q-page>
+    <div
+      v-if="renderComponent"
+      id="container"
+      style="width: 100%"
+      ref="pay"
+    ></div>
+  </q-page>
+</template>
+
+<script>
+import { onMounted, nextTick, ref } from "vue";
+import { orderPaymentParamStore } from "stores/orderPaymentParamStore";
+export default {
+  name: "AlipayPage",
+  setup() {
+    const oStore = orderPaymentParamStore();
+
+    const codeUrl = ref("");
+    const orderId = ref("");
+    const price = ref("");
+
+    const htmlContent = ref("");
+
+    const renderComponent = ref(true);
+
+    function b64_to_utf8(str) {
+      return window.atob(str);
+    }
+
+    const forceRerender = async () => {
+      renderComponent.value = false;
+      // Wait for the change to get flushed to the DOM
+      await nextTick();
+
+      // Add the component back in
+      renderComponent.value = true;
+    };
+
+    onMounted(() => {
+      var allParamsFromPreviousPage = oStore.getPaymentDetail;
+      console.log("what I got from book form:");
+      console.log(allParamsFromPreviousPage);
+      codeUrl.value = allParamsFromPreviousPage.codeUrl;
+      console.log("decoded");
+      htmlContent.value = b64_to_utf8(codeUrl.value);
+      console.log(htmlContent.value);
+      orderId.value = allParamsFromPreviousPage.orderId;
+      price.value = allParamsFromPreviousPage.price;
+
+      // forceRerender();
+      // var container = document.getElementById("container");
+      // container.innerHTML += htmlContent.value;
+      // container.innerHTML += "<p>this is a test text</p>";
+
+      document.open();
+      document.write(htmlContent.value);
+      document.close();
+      forceRerender();
+    });
+
+    return {
+      renderComponent,
+      htmlContent,
+    };
+  },
+};
+</script>

+ 245 - 0
hichina-main-front-mobile-first-v2/src/pages/BlogCreatePage.vue

@@ -0,0 +1,245 @@
+<template>
+  <q-page>
+    <div class="row justify-center">
+      <div class="col-10">
+        <div class="col-12 q-mt-xl">
+          <q-input rounded outlined v-model="title" label="Blog title" />
+        </div>
+        <div
+          class="col-12 q-mt-xl"
+          style="height: 50vh"
+          v-if="isClient & RichEditorLoaded & ImageUploaderLoaded"
+        >
+          <!-- <QuillEditor
+            theme="snow"
+            style="height: 100%"
+            v-model:content="content"
+            contentType="html"
+            toolbar="full"
+            :modules="combineModule"
+          /> -->
+          <component
+            :is="RichEditorComponent"
+            theme="snow"
+            style="height: 100%"
+            v-model:content="content"
+            contentType="html"
+            toolbar="full"
+            :modules="combineModule"
+          />
+        </div>
+        <div class="col-12 q-mt-xl row q-mb-xl">
+          <div class="col-6 row justify-center">
+            <q-btn
+              @click="postBlog(false)"
+              color="grey-4"
+              text-color="primary"
+              glossy
+              unelevated
+              icon="publish"
+              label="Publish"
+            />
+          </div>
+          <div class="col-6 row justify-center">
+            <q-btn
+              @click="postBlog(true)"
+              color="grey-4"
+              text-color="primary"
+              glossy
+              unelevated
+              icon="drafts"
+              label="Draft"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, onBeforeMount, getCurrentInstance } from "vue";
+// import { QuillEditor } from "@vueup/vue-quill";
+import "@vueup/vue-quill/dist/vue-quill.snow.css";
+// import ImageUploader from "quill-image-uploader";
+import { useQuasar } from "quasar";
+import { api } from "boot/axios";
+export default {
+  name: "BlogCreatePage",
+  // components: {
+  //   QuillEditor,
+  // },
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const isClient = ref(false);
+    const content = ref("");
+    const title = ref("");
+
+    const RichEditorComponent = ref({});
+    const RichEditorLoaded = ref(false);
+    const ImageUploaderLoaded = ref(false);
+
+    // const imageUploadModule = {
+    //   name: "imageUploader",
+    //   module: ImageUploader,
+    //   options: {
+    //     upload: (file) => {
+    //       return new Promise((resolve, reject) => {
+    //         const formData = new FormData();
+    //         formData.append("imageFile", file);
+    //         formData.append("expectedType", "blogImage");
+
+    //         gp.$showLoading($q);
+    //         api
+    //           .post("/api/v1/image/upload", formData)
+    //           .then((res) => {
+    //             resolve(res.data.data);
+    //             gp.$hideLoading($q);
+    //           })
+    //           .catch((err) => {
+    //             gp.$hideLoading($q);
+    //             reject("Upload failed");
+    //             console.error("Error:", err);
+    //           });
+    //       });
+    //     },
+    //   },
+    // };
+
+    // const combineModule = [imageUploadModule];
+    const combineModule = ref({});
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in setup: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+
+    function postBlog(shouldDraft) {
+      if (
+        content.value == null ||
+        content.value.length < 1 ||
+        title.value == null ||
+        title.value.length < 1
+      ) {
+        gp.$generalNotify($q, false, "Title or content cannot be empty");
+        return;
+      }
+
+      const params = {};
+      params.draft = shouldDraft;
+      params.title = title.value;
+      params.content = content.value;
+      params.headImageUrl = "";
+
+      gp.$showLoading($q);
+
+      api
+        .post("/api/v1/blog", params)
+        .then((res) => {
+          console.log(res.data.data.blogId);
+          gp.$hideLoading($q);
+          gp.$goPage("/blog-detail/" + res.data.data.blogId);
+        })
+        .catch((err) => {
+          gp.$hideLoading($q);
+          gp.$generalNotify($q, false, err);
+          console.error("Error:", err);
+          if (err.response.status == 401) {
+            gp.$goPage("/auth/login");
+          }
+        });
+    }
+
+    onBeforeMount(() => {
+      isClient.value = process.env.CLIENT;
+
+      if (process.env.CLIENT) {
+        import("@vueup/vue-quill")
+          .then((module) => {
+            // Use the module here
+            console.log("@vueup/vue-quill loaded");
+            console.log(module.QuillEditor);
+            RichEditorComponent.value = module.QuillEditor;
+            RichEditorLoaded.value = true;
+          })
+          .catch((error) => {
+            console.error("Error importing module ckeditor5-vue:", error);
+          });
+
+        import("quill-image-uploader")
+          .then((module) => {
+            // Use the module here
+            console.log("quill-image-uploader loaded");
+            console.log(module.default);
+            // ??.value = module.default;
+
+            const imageUploadModule = {
+              name: "imageUploader",
+              module: module.default,
+              options: {
+                upload: (file) => {
+                  return new Promise((resolve, reject) => {
+                    const formData = new FormData();
+                    formData.append("imageFile", file);
+                    formData.append("expectedType", "blogImage");
+
+                    gp.$showLoading($q);
+                    api
+                      .post("/api/v1/image/upload", formData)
+                      .then((res) => {
+                        resolve(res.data.data);
+                        gp.$hideLoading($q);
+                      })
+                      .catch((err) => {
+                        gp.$hideLoading($q);
+                        reject("Upload failed");
+                        console.error("Error:", err);
+                      });
+                  });
+                },
+              },
+            };
+
+            combineModule.value = [imageUploadModule];
+
+            ImageUploaderLoaded.value = true;
+          })
+          .catch((error) => {
+            console.error("Error importing module ckeditor5-vue:", error);
+          });
+      }
+    });
+
+    onMounted(() => {
+      whoami();
+    });
+    return {
+      ImageUploaderLoaded,
+      RichEditorLoaded,
+      RichEditorComponent,
+      isClient,
+      content,
+      combineModule,
+      postBlog,
+      title,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.ql-clipboard
+  position: fixed !important
+  opacity: 0 !important
+  left: 50% !important
+  top: 50% !important
+</style>

+ 265 - 0
hichina-main-front-mobile-first-v2/src/pages/BlogDetailPage.vue

@@ -0,0 +1,265 @@
+<template>
+  <q-page>
+    <div class="row justify-center">
+      <div class="col-12 col-md-9" style="min-height: 500px">
+        <div class="text-weight-bold text-h3 q-pa-xl">
+          {{ removeHtmlTag(title) }}
+        </div>
+        <div class="row col-12" style="min-height: 50px">
+          <div class="q-ml-xl">
+            <q-avatar size="86px">
+              <img :src="authorProfileImageUrl" />
+            </q-avatar>
+          </div>
+          <div class="col-3 q-ml-md">
+            <div class="text-weight-bold text-h6">{{ username }}</div>
+            <div><q-btn color="primary" icon="add" label="Follow" /></div>
+          </div>
+          <div class="col q-mt-md">
+            <q-btn
+              icon="g_translate"
+              @click="translateThisArticle(content)"
+              label="Translate"
+              dense
+              rounded
+              stack
+              glossy
+              color="primary"
+            />
+          </div>
+        </div>
+        <div class="row q-px-xl q-py-md text-h5">
+          <div class="col-5">{{ createdTime }}</div>
+          <div class="col-3">
+            <q-icon color="blue" name="visibility" />{{ pageViewCnt }}
+          </div>
+          <div class="col-4">
+            Collect this<q-btn
+              class="q-ml-sm"
+              round
+              color="primary"
+              icon="bookmark"
+              size="xs"
+            />
+          </div>
+        </div>
+        <div
+          class="q-px-xl q-pt-md"
+          style="border-top: 1px solid black"
+          v-html="content"
+        ></div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance, reactive } from "vue";
+import { api } from "boot/axios";
+import { useRoute } from "vue-router";
+import { useQuasar } from "quasar";
+import { useSeoMeta } from "unhead";
+import { useI18n } from "vue-i18n";
+
+export default {
+  name: "BlogDetail",
+  setup() {
+    const { locale } = useI18n({ useScope: "global" });
+
+    const route = useRoute();
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const pageViewCnt = ref(0);
+    const authorProfileImageUrl = ref("");
+    const title = ref("");
+    const username = ref("");
+    const content = ref("");
+    const createdTime = ref("");
+
+    const siteData = reactive({
+      title: "My Awesome Blog",
+      shortContent: "Hichina Travel Blog",
+    });
+
+    function logView() {
+      api
+        .post("/api/public/pagestats/view-blog/" + route.params.blogId)
+        .then((res) => {
+          console.log("view cnt of this blog:");
+          console.log(res.data);
+          pageViewCnt.value = res.data.data;
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    function toBaiduLanguage(input) {
+      if (input === "ko-KR") {
+        return "kor";
+      } else if (input === "en-US") {
+        return "en";
+      } else if (input === "ru-RU") {
+        return "ru";
+      } else if (input === "th-TH") {
+        return "th";
+      } else {
+        return "auto";
+      }
+    }
+
+    function translateTitle() {
+      const params = {};
+      // key format: type_guid_part_tolang example: blog_f4128363-84bb-4169-a30c-a3d6fbc0a3ef_title_th
+      params.translationKey =
+        "blog_" +
+        route.params.blogId +
+        "_title_" +
+        toBaiduLanguage(locale.value);
+      params.query = title.value;
+      params.from = "auto";
+      params.to = toBaiduLanguage(locale.value);
+      gp.$showLoading($q);
+      api
+        .post("/api/public/baidutranslate", params)
+        .then((res) => {
+          console.log("baidu translate result title: ");
+          console.log(res.data);
+          if (res.data.ok == true) {
+            if (res.data.data != null && res.data.data.length > 0) {
+              title.value = res.data.data;
+            }
+            gp.$generalNotify($q, true, res.data.message);
+          } else {
+            gp.$generalNotify($q, false, res.data.message);
+            console.log("translate title failed");
+          }
+
+          gp.$hideLoading($q);
+        })
+        .catch((err) => {
+          console.log("err baidu translate title");
+          gp.$generalNotify($q, false, "err baidu translate title");
+          gp.$hideLoading($q);
+        });
+    }
+
+    function translateContent() {
+      const params = {};
+      // key format: type_guid_part_tolang example: blog_f4128363-84bb-4169-a30c-a3d6fbc0a3ef_content_th
+      params.translationKey =
+        "blog_" +
+        route.params.blogId +
+        "_content_" +
+        toBaiduLanguage(locale.value);
+      params.query = content.value;
+      params.from = "auto";
+      params.to = toBaiduLanguage(locale.value);
+      gp.$showLoading($q);
+      api
+        .post("/api/public/baidutranslate", params)
+        .then((res) => {
+          console.log("baidu translate result: ");
+          console.log(res.data);
+          if (res.data.ok == true) {
+            if (res.data.data != null && res.data.data.length > 0) {
+              content.value = res.data.data;
+            }
+            gp.$generalNotify($q, true, res.data.message);
+            translateTitle();
+          } else {
+            console.log("translate failed");
+            gp.$generalNotify($q, false, res.data.message);
+          }
+
+          gp.$hideLoading($q);
+        })
+        .catch((err) => {
+          console.log("err baidu translate2");
+          gp.$generalNotify($q, false, "err baidu translate");
+          gp.$hideLoading($q);
+        });
+    }
+    function translateThisArticle(textToTranslate) {
+      translateContent();
+    }
+
+    function getAuthorPrincipal() {
+      api
+        .get(
+          "/api/public/blog/blog-author-profile-image/" + route.params.blogId
+        )
+        .then(function (response) {
+          console.log("blog author profile image");
+          console.log(response.data);
+          if (response.data.ok == true) {
+            authorProfileImageUrl.value = response.data.data;
+            if (
+              authorProfileImageUrl.value == null ||
+              authorProfileImageUrl.value.length < 1
+            ) {
+              authorProfileImageUrl.value =
+                "https://photoprism.hichinatravel.com/api/v1/t/8623903789c65a160279faa0b33159413cb18af4/32mcf2k4/fit_2048";
+            }
+          } else {
+            gp.$generalNotify($q, false, response.data.message);
+          }
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadBlogDetail() {
+      api
+        .get("/api/public/blog/" + route.params.blogId)
+        .then((response) => {
+          title.value = response.data.data.title;
+          username.value = response.data.data.authorName;
+          content.value = response.data.data.content;
+          createdTime.value = response.data.data.createdTime;
+
+          // set head meta for seo
+          // Set the title meta tag
+          siteData.title = title.value;
+          siteData.shortContent = gp
+            .$removeHtmlTag(content.value)
+            .substring(0, 50);
+
+          useSeoMeta({
+            title: siteData.title,
+            description: siteData.shortContent,
+            ogDescription: siteData.shortContent,
+            ogTitle: siteData.title,
+            ogImage:
+              "https://www.hichinatravel.com/static/png/name-67280b81.png",
+            twitterCard: "summary_large_image",
+          });
+        })
+        .catch((e) => {
+          console.log("get blog detail err");
+          console.log(e);
+        });
+    }
+
+    onMounted(() => {
+      logView();
+      getAuthorPrincipal();
+      loadBlogDetail();
+    });
+
+    return {
+      title,
+      authorProfileImageUrl,
+      username,
+      createdTime,
+      pageViewCnt,
+      content,
+      translateThisArticle,
+    };
+  },
+};
+</script>

+ 263 - 0
hichina-main-front-mobile-first-v2/src/pages/BlogEditPage.vue

@@ -0,0 +1,263 @@
+<template>
+  <q-page>
+    <div class="row justify-center">
+      <div class="col-10">
+        <div class="col-12 q-mt-xl">
+          <q-input rounded outlined v-model="title" label="Blog title" />
+        </div>
+        <div
+          class="col-12 q-mt-xl"
+          style="height: 50vh"
+          v-if="isClient & RichEditorLoaded & ImageUploaderLoaded"
+        >
+          <!-- <QuillEditor
+            @paste="handlePaste"
+            theme="snow"
+            v-model:content="content"
+            contentType="html"
+            toolbar="full"
+            style="height: 100%"
+            ref="quillEditor"
+            :modules="combineModule"
+          /> -->
+          <component
+            :is="RichEditorComponent"
+            @paste="handlePaste"
+            theme="snow"
+            v-model:content="content"
+            contentType="html"
+            toolbar="full"
+            style="height: 100%"
+            ref="quillEditor"
+            :modules="combineModule"
+          />
+        </div>
+        <div class="col-12 q-mt-xl row q-mb-xl">
+          <div class="col-6 row justify-left">
+            <q-btn
+              @click="updateBlog()"
+              color="grey-4"
+              text-color="primary"
+              glossy
+              unelevated
+              icon="update"
+              label="Update"
+            />
+          </div>
+          <div class="col-6 row justify-left">
+            <q-btn
+              @click="goPage('/blog-detail/' + currentBlogId)"
+              color="grey-4"
+              text-color="primary"
+              glossy
+              unelevated
+              icon="visibility"
+              label="View Blog"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, onBeforeMount, getCurrentInstance } from "vue";
+import { useQuasar } from "quasar";
+// import { QuillEditor } from "@vueup/vue-quill";
+import "@vueup/vue-quill/dist/vue-quill.snow.css";
+// import ImageUploader from "quill-image-uploader";
+import { api } from "boot/axios";
+import { useRoute } from "vue-router";
+export default {
+  name: "BlogEditPage",
+  // components: {
+  //   QuillEditor,
+  // },
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const ImageUploaderLoaded = ref(false);
+    const route = useRoute();
+    const RichEditorLoaded = ref(false);
+
+    const title = ref("");
+    const content = ref("");
+    const savedScrollPosition = ref(null);
+
+    const currentBlogId = ref("");
+
+    const isClient = ref(false);
+    currentBlogId.value = route.params.blogId;
+
+    function handlePaste() {}
+
+    // const imageUploadModule = {
+    //   name: "imageUploader",
+    //   module: ImageUploader,
+    //   options: {
+    //     upload: (file) => {
+    //       return new Promise((resolve, reject) => {
+    //         const formData = new FormData();
+    //         formData.append("imageFile", file);
+    //         formData.append("expectedType", "blogImage");
+
+    //         gp.$showLoading($q);
+    //         api
+    //           .post("/api/v1/image/upload", formData)
+    //           .then((res) => {
+    //             resolve(res.data.data);
+    //             gp.$hideLoading($q);
+    //           })
+    //           .catch((err) => {
+    //             gp.$hideLoading($q);
+    //             reject("Upload failed");
+    //             console.error("Error:", err);
+    //           });
+    //       });
+    //     },
+    //   },
+    // };
+
+    // const combineModule = [imageUploadModule];
+
+    const combineModule = ref({});
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in setup: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+
+    function loadBlogDetail(blogId) {
+      api
+        .get("/api/v1/blog/" + blogId)
+        .then(function (response) {
+          console.log("blog detail:");
+          console.log(response.data);
+          title.value = response.data.data.title;
+          content.value = response.data.data.content;
+        })
+        .catch(function (error) {
+          console.log("currently not logged in blog edit: " + error);
+        });
+    }
+    function updateBlog() {
+      const params = {};
+      params.title = title.value;
+      params.content = content.value;
+      params.headImageUrl = "";
+
+      gp.$showLoading($q);
+      api
+        .put("/api/v1/blog/edit-basic/" + route.params.blogId, params)
+        .then((res) => {
+          gp.$hideLoading($q);
+          gp.$generalNotify($q, true, "Succeed updating blogs");
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+          gp.$hideLoading($q);
+          gp.$generalNotify($q, false, "Error:", err);
+          if (err.response.status == 401) {
+            gp.$goPage("/auth/login");
+          }
+        });
+    }
+
+    onBeforeMount(() => {
+      isClient.value = process.env.CLIENT;
+
+      if (process.env.CLIENT) {
+        import("@vueup/vue-quill")
+          .then((module) => {
+            // Use the module here
+            console.log("@vueup/vue-quill loaded");
+            console.log(module.QuillEditor);
+            RichEditorComponent.value = module.QuillEditor;
+            RichEditorLoaded.value = true;
+          })
+          .catch((error) => {
+            console.error("Error importing module ckeditor5-vue:", error);
+          });
+
+        import("quill-image-uploader")
+          .then((module) => {
+            // Use the module here
+            console.log("quill-image-uploader loaded");
+            console.log(module.default);
+            // ??.value = module.default;
+
+            const imageUploadModule = {
+              name: "imageUploader",
+              module: module.default,
+              options: {
+                upload: (file) => {
+                  return new Promise((resolve, reject) => {
+                    const formData = new FormData();
+                    formData.append("imageFile", file);
+                    formData.append("expectedType", "blogImage");
+
+                    gp.$showLoading($q);
+                    api
+                      .post("/api/v1/image/upload", formData)
+                      .then((res) => {
+                        resolve(res.data.data);
+                        gp.$hideLoading($q);
+                      })
+                      .catch((err) => {
+                        gp.$hideLoading($q);
+                        reject("Upload failed");
+                        console.error("Error:", err);
+                      });
+                  });
+                },
+              },
+            };
+
+            combineModule.value = [imageUploadModule];
+
+            ImageUploaderLoaded.value = true;
+          })
+          .catch((error) => {
+            console.error("Error importing module ckeditor5-vue:", error);
+          });
+      }
+    });
+
+    onMounted(() => {
+      whoami();
+
+      loadBlogDetail(route.params.blogId);
+    });
+
+    return {
+      RichEditorLoaded,
+      isClient,
+      ImageUploaderLoaded,
+      title,
+      content,
+      updateBlog,
+      handlePaste,
+      currentBlogId,
+      combineModule,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.ql-clipboard
+  position: fixed !important
+  opacity: 0 !important
+  left: 50% !important
+  top: 50% !important
+</style>

+ 221 - 0
hichina-main-front-mobile-first-v2/src/pages/BlogPage.vue

@@ -0,0 +1,221 @@
+<template>
+  <q-page>
+    <div
+      class="row justify-center text-h4 text-weight-bold text-blue-6 q-mt-md"
+    >
+      {{ $t("blogs_of_the_week") }}
+    </div>
+    <div class="q-pa-md">
+      <q-carousel
+        animated
+        v-model="slide"
+        navigation
+        infinite
+        height="540px"
+        :autoplay="autoplay"
+        arrows
+        transition-prev="slide-right"
+        transition-next="slide-left"
+        @mouseenter="autoplay = false"
+        @mouseleave="autoplay = 2000"
+      >
+        <q-carousel-slide
+          v-for="(item, index) in sliders"
+          :key="index"
+          :name="index"
+          :img-src="item.imageUrl"
+          class="cursor-pointer"
+          @click="gotoUrl(item.linkToBlog)"
+        >
+          <div class="q-mt-xl q-ml-xl absolute-left custom-caption">
+            <div style="height: 100px"></div>
+            <div class="text-h1 text-white text-weight-bolder">
+              {{ item.title }}
+            </div>
+            <div class="text-h5 text-white text-weight-medium">
+              {{ item.subTitle }}
+            </div>
+          </div>
+        </q-carousel-slide>
+      </q-carousel>
+    </div>
+    <div class="row justify-center q-pa-md">
+      <q-btn
+        rounded
+        color="primary"
+        size="xl"
+        @click="goPage('/blog-create')"
+        :label="$t('write_your_blog')"
+      />
+    </div>
+    <div class="row q-pa-md">
+      <div class="q-gutter-y-md col-12 col-md-8">
+        <q-tabs v-model="tab" dense align="justify" class="text-primary">
+          <q-tab :ripple="false" name="lb" :label="$t('latest_blogs')" />
+          <q-tab
+            :ripple="false"
+            name="mvm"
+            :label="$t('most_viewed_in_month')"
+          />
+          <q-tab
+            :ripple="false"
+            name="bfl"
+            :label="$t('blogers_m_following')"
+          />
+        </q-tabs>
+      </div>
+    </div>
+    <div class="row justify-left">
+      <div
+        class="col-12 col-sm-4 col-md-2"
+        v-for="(item, index) in globalUnifiedItemList"
+        v-bind:key="index"
+      >
+        <div class="q-pa-md">
+          <q-card class="cursor-pointer" flat bordered style="height: 530px">
+            <q-img
+              @click="goPage('/blog-detail/' + item.value.blogId)"
+              :src="item.value.headImageUrl"
+              style="height: 300px"
+              placeholder-src="https://photoprism.hichinatravel.com/api/v1/t/2bfc32550ae040956f7e861566d26c487c0143e7/32mcf2k4/tile_224"
+            />
+
+            <q-card-section style="max-height: 200px; overflow: hidden">
+              <div class="text-overline text-orange-9">
+                {{ item.value.createdTime }}
+              </div>
+              <div class="text-h5 q-mt-sm q-mb-xs">
+                <a :href="'./blog-detail/' + item.value.blogId">{{
+                  item.value.title
+                }}</a>
+              </div>
+              <div class="text-caption text-grey">
+                {{ item.value.content }}
+              </div>
+            </q-card-section>
+          </q-card>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-12">
+        <p class="text-center" style="background-color: #b4b4b4">
+          Scroll to load more
+        </p>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { api } from "boot/axios";
+import { debounce } from "lodash";
+
+export default {
+  name: "BlogPage",
+  setup() {
+    const unifiedItemList = ref([]);
+    const blogPageSizePage = ref(20);
+    const currentPage = ref(1);
+    const bloglist = ref([]);
+    const sliders = ref([]);
+    const totalBlogCount = ref(0);
+    const globalUnifiedItemList = ref([]);
+
+    function loadBlogList() {
+      unifiedItemList.value = [];
+      var params = {};
+      params.pageSize = blogPageSizePage.value;
+      params.query = "";
+      params.page = currentPage.value;
+      api
+        .get("/api/public/blog/list", { params: params })
+        .then(function (response) {
+          bloglist.value = response.data.data.data;
+          totalBlogCount.value = response.data.data.total;
+          for (var index in bloglist.value) {
+            var obj = {};
+            // obj.type=parseInt(Math.random() * 2)==0?'blog':'scaleblog';
+            obj.type = "blog";
+            obj.value = bloglist.value[index];
+            unifiedItemList.value.push(obj);
+          }
+          globalUnifiedItemList.value = globalUnifiedItemList.value.concat(
+            unifiedItemList.value
+          );
+          console.log("globalUnifiedItemList");
+          console.log(globalUnifiedItemList.value);
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadSliders() {
+      api
+        .get("/api/public/pagecontent/bloghomesliders")
+        .then(function (response) {
+          console.log("blog sliders:");
+          sliders.value = response.data.data;
+          console.log(sliders.value);
+          loadBlogList();
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadMore() {
+      currentPage.value += 1;
+      var maxPage = totalBlogCount.value / blogPageSizePage.value;
+      if (currentPage.value <= maxPage + 1) {
+        loadBlogList();
+      }
+    }
+
+    function getNextBatch() {
+      window.onscroll = debounce(function () {
+        let bottomOfWindow =
+          document.documentElement.scrollTop + window.innerHeight + 100 >
+          document.documentElement.scrollHeight;
+
+        if (bottomOfWindow) {
+          loadMore();
+        }
+      }, 500);
+    }
+    function logPv() {
+      api
+        .post("/api/public/pagestats/pv/blog")
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    onMounted(() => {
+      logPv();
+      loadSliders();
+      getNextBatch();
+    });
+
+    return {
+      slide: ref(1),
+      autoplay: ref(true),
+      sliders,
+      tab: ref("lb"),
+      globalUnifiedItemList,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.custom-caption
+  text-align: left
+  padding: 12px
+  color: white
+</style>

+ 474 - 0
hichina-main-front-mobile-first-v2/src/pages/BookPage.vue

@@ -0,0 +1,474 @@
+<template>
+  <q-page>
+    <div class="row" style="height: 50px; background-color: #e5f2fa">
+      <div class="col-sm-1"></div>
+      <div
+        class="col-12 col-sm flex text-subtitle1"
+        style="align-items: center"
+      >
+        Travel shop > {{ productName }}
+      </div>
+    </div>
+    <div class="row text-weight-bold text-h5 q-pa-md">
+      Product Name: {{ productName }}
+    </div>
+    <div class="text-body1 q-px-md">
+      <div>Package Category: {{ packageCategory }}</div>
+      <div v-if="selectedDate != null && selectedDate.length > 0">
+        Start Date: {{ selectedDate }}
+      </div>
+      <div v-if="adultCount != null && adultCount > 0">
+        Adults: {{ adultCount }}
+      </div>
+      <div v-if="childCount != null && childCount > 0">
+        Child: {{ childCount }}
+      </div>
+      <div v-if="infantCount != null && infantCount > 0">
+        Infant: {{ infantCount }}
+      </div>
+      <div v-if="buyCount != null && buyCount > 0">
+        Purchase Count: {{ buyCount }}
+      </div>
+    </div>
+    <div class="row">
+      <div
+        class="q-pa-md col-12"
+        v-for="index in parseInt(adultCount) +
+        parseInt(childCount) +
+        parseInt(infantCount)"
+        :key="index"
+      >
+        <div class="text-weight-bold text-h6">Traveler: {{ index }}</div>
+        <div class="row">
+          <q-input
+            class="col-12 col-sm-6 col-md-4"
+            rounded
+            dense
+            outlined
+            v-model="peopleform.value[index - 1]['surName']"
+            label="Surname*"
+            :rules="[(val) => !!val || 'Field is required']"
+          />
+        </div>
+        <div class="row">
+          <q-input
+            class="col-12 col-sm-6 col-md-4"
+            rounded
+            dense
+            outlined
+            v-model="peopleform.value[index - 1]['givenName']"
+            label="Given Name*"
+            :rules="[(val) => !!val || 'Field is required']"
+          />
+        </div>
+        <div>
+          <div class="q-pb-sm text-weight-bold">Birthday</div>
+          <q-date
+            mask="YYYY-MM-DD"
+            v-model="peopleform.value[index - 1]['birthday']"
+          />
+        </div>
+        <div class="row q-mt-md">
+          <q-select
+            outlined
+            dense
+            emit-value
+            map-options
+            class="col-12 col-sm-6 col-md-4"
+            v-model="peopleform.value[index - 1]['gender']"
+            :options="genderOption"
+            option-value="value"
+            option-label="label"
+            label="Gender"
+          />
+        </div>
+        <div class="row q-mt-md">
+          Nationality:
+          <country-select
+            v-model="peopleform.value[index - 1]['nationality']"
+            :country="country"
+            topCountry="US"
+          />
+        </div>
+        <div class="row q-mt-md">
+          <q-input
+            class="col-12 col-sm-6 col-md-4"
+            rounded
+            dense
+            outlined
+            v-model="peopleform.value[index - 1]['passportNo']"
+            label="Passport Number*"
+            :rules="[(val) => !!val || 'Field is required']"
+          />
+        </div>
+        <div>
+          <div class="q-pb-sm text-weight-bold">Expiry date of Passport</div>
+          <q-date
+            mask="YYYY-MM-DD"
+            v-model="peopleform.value[index - 1]['passportExpireDate']"
+          />
+        </div>
+      </div>
+    </div>
+    <div class="row text-h6 text-weight-bold q-pa-md">Contact Info</div>
+    <div class="row">
+      <q-input
+        class="q-pa-md col-12 col-sm-6 col-md-4"
+        dense
+        outlined
+        v-model="contactform['name']"
+        label="Name*"
+        :rules="[(val) => !!val || 'Field is required']"
+      />
+    </div>
+    <div class="row">
+      <q-input
+        class="q-pa-md col-12 col-sm-6 col-md-4"
+        dense
+        outlined
+        v-model="contactform['email']"
+        label="Email Address*"
+        :rules="[(val) => !!val || 'Field is required']"
+      />
+    </div>
+    <div class="row">
+      <q-input
+        class="q-pa-md col-12 col-sm-6 col-md-4"
+        dense
+        outlined
+        v-model="contactform['phone']"
+        label="Phone Number*"
+        :rules="[(val) => !!val || 'Field is required']"
+      />
+    </div>
+    <div class="row">
+      <q-input
+        class="q-pa-md col-12 col-sm-6 col-md-4"
+        dense
+        outlined
+        v-model="contactform['address']"
+        label="Address in China*"
+        :rules="[(val) => !!val || 'Field is required']"
+      />
+    </div>
+    <div class="row text-h6 text-weight-bold q-pa-md">Coupon Code</div>
+    <div class="row">
+      <q-input
+        class="q-px-md col-12 col-sm-6 col-md-4"
+        dense
+        outlined
+        v-model="coupon"
+        label="Have Coupon? Enter code here"
+      />
+    </div>
+    <div class="row q-pa-md">
+      <div class="row col-6">
+        <div
+          class="col-6 col-sm-4 text-blue-6 text-h5 q-py-md"
+          style="border-bottom: 1px solid black"
+        >
+          Payment CNY:
+        </div>
+        <div class="col-6 col-sm-8 text-red text-h3 q-py-md">
+          {{ totalPrice }}¥
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <q-btn
+        icon="lab la-alipay"
+        glossy
+        color="blue-6"
+        label="Pay with Alipay"
+        class="q-ma-sm"
+        @click="submitOrder('alipay')"
+      />
+      <q-btn
+        icon="lab la-weixin"
+        glossy
+        color="green-6"
+        label="Pay with Wechat Pay"
+        class="q-ma-sm"
+        @click="submitOrder('wechatpay')"
+      />
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance, reactive } from "vue";
+import { useQuasar } from "quasar";
+import { useRoute } from "vue-router";
+import { api } from "boot/axios";
+import { bookParamStore } from "stores/bookParamStore";
+import { orderPaymentParamStore } from "stores/orderPaymentParamStore";
+
+export default {
+  name: "BookPage",
+  setup() {
+    const bStore = bookParamStore();
+    const oStore = orderPaymentParamStore();
+
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const route = useRoute();
+
+    const coupon = ref("");
+    const country = ref("");
+    const productName = ref("");
+    const productTypeId = ref("");
+    const productSkuId = ref("");
+    const packageCategory = ref("");
+    const selectedDate = ref(new Date());
+    const adultCount = ref(0);
+    const childCount = ref(0);
+    const infantCount = ref(0);
+    const buyCount = ref(1);
+    const totalPrice = ref(0);
+
+    const peopleform = reactive([]);
+    const contactform = ref({});
+
+    const LOCALSPECIALTYPRODUCTTYPE = "fd264cab-ee8d-4571-a477-03d7e7c090b3";
+
+    function checkPassenger(passenger) {
+      if (
+        gp.$checkEmpty(passenger.birthday) ||
+        gp.$checkEmpty(passenger.gender) ||
+        gp.$checkEmpty(passenger.givenName) ||
+        gp.$checkEmpty(passenger.surName) ||
+        gp.$checkEmpty(passenger.nationality) ||
+        gp.$checkEmpty(passenger.passportExpireDate) ||
+        gp.$checkEmpty(passenger.passportNo) ||
+        gp.$checkEmpty(passenger.surName)
+      ) {
+        return false;
+      }
+      return true;
+    }
+
+    function validatePreOrderParams(finalParams) {
+      console.log("validating finalParams");
+      console.log(finalParams);
+      // must have CNY value valid
+      if (
+        gp.$checkEmpty(finalParams.productInfo["totalPrice"]) ||
+        !Number.isInteger(parseInt(finalParams.productInfo["totalPrice"]))
+      ) {
+        gp.$generalNotify($q, false, "something wrong with price");
+        return false;
+      }
+      // must have full contact info
+      if (
+        gp.$checkEmpty(finalParams.contactInfo["email"]) ||
+        gp.$checkEmpty(finalParams.contactInfo["phone"]) ||
+        gp.$checkEmpty(finalParams.contactInfo["name"]) ||
+        gp.$checkEmpty(finalParams.contactInfo["address"])
+      ) {
+        gp.$generalNotify(
+          $q,
+          false,
+          "Please check your contact info is filled correctly"
+        );
+        return false;
+      }
+      // must have full product info
+      if (gp.$checkEmpty(finalParams.productInfo)) {
+        gp.$generalNotify(
+          $q,
+          false,
+          "Please check your product info is correct"
+        );
+        return false;
+      }
+      // must have full passenger info if not localspecialty type, if no passenger , no check
+      for (var index in finalParams.passengerInfo) {
+        if (!checkPassenger(finalParams.passengerInfo[index])) {
+          gp.$generalNotify(
+            $q,
+            false,
+            "Please check your passenger info has been filled correctly"
+          );
+          return false;
+        }
+      }
+
+      return true;
+    }
+
+    function submitOrder(paymethod) {
+      // if (paymethod == "alipay") {
+      //   alert("Feature not available yet, will be online soon!");
+      //   return;
+      // }
+      var finalParams = {};
+      if (LOCALSPECIALTYPRODUCTTYPE != productTypeId.value) {
+        finalParams.passengerInfo = peopleform.value;
+      }
+      finalParams.contactInfo = contactform.value;
+      finalParams.coupon = coupon.value;
+      finalParams.productInfo = bStore.getOrderDetail;
+      console.log("finalParams");
+      console.log(finalParams);
+      if (validatePreOrderParams(finalParams)) {
+        // do submit
+        var params = {};
+        params.meta = JSON.stringify(finalParams);
+        params.skuId = productSkuId.value;
+        params.status = "SUBMITTED";
+        params.price = totalPrice.value;
+        params.payMethod = paymethod;
+        api
+          .post("/api/v1/order", params)
+          .then((res) => {
+            gp.$generalNotify($q, true, "Succeed creating order");
+            if (paymethod == "wechatpay") {
+              // go to we chat pay page
+              // go to alipay page
+              console.log(res.data.data);
+              var param2Pass = {};
+              param2Pass.price = res.data.data.price;
+              param2Pass.orderId = res.data.data.orderId;
+              param2Pass.codeUrl = res.data.data.codeUrl;
+              param2Pass.productName = productName.value;
+
+              oStore.setPaymentDetail(param2Pass);
+              gp.$goPage("/wechatpay");
+            } else if (paymethod == "alipay") {
+              // go to alipay page
+              console.log(res.data.data);
+              var param2Pass = {};
+              param2Pass.price = res.data.data.price;
+              param2Pass.orderId = res.data.data.orderId;
+              param2Pass.codeUrl = res.data.data.codeUrl;
+
+              oStore.setPaymentDetail(param2Pass);
+              gp.$goPage("/alipay");
+            }
+          })
+          .catch((err) => {
+            gp.$generalNotify($q, false, "Fail creating order" + err);
+          });
+      }
+    }
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in book page: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+
+    function todayString() {
+      const today = new Date();
+      const year = today.getFullYear();
+      const month = String(today.getMonth() + 1).padStart(2, "0");
+      const day = String(today.getDate()).padStart(2, "0");
+      return `${year}-${month}-${day}`;
+    }
+
+    function initPeopleform() {
+      peopleform.value = [];
+
+      for (
+        let index = 0;
+        index <
+        parseInt(adultCount.value) +
+          parseInt(childCount.value) +
+          parseInt(infantCount.value);
+        index++
+      ) {
+        peopleform.value.push({
+          surName: "",
+          givenName: "",
+          birthday: ref(todayString()),
+          gender: "",
+          nationality: "",
+          passportNo: "",
+          passportExpireDate: ref(todayString()),
+        });
+      }
+    }
+    onMounted(() => {
+      whoami();
+      var allParamsFromPreviousPage = bStore.getOrderDetail;
+
+      console.log("allParamsFromPreviousPage");
+      console.log(allParamsFromPreviousPage);
+      if (
+        allParamsFromPreviousPage.productName == null ||
+        allParamsFromPreviousPage.productName.length < 1 ||
+        allParamsFromPreviousPage.productSkuId == null ||
+        allParamsFromPreviousPage.productSkuId.length < 1 ||
+        allParamsFromPreviousPage.packageCategory == null ||
+        allParamsFromPreviousPage.packageCategory.length < 1
+      ) {
+        gp.$goPage("/");
+        return;
+      }
+
+      productName.value = allParamsFromPreviousPage.productName;
+      productSkuId.value = allParamsFromPreviousPage.productSkuId;
+      packageCategory.value = allParamsFromPreviousPage.packageCategory;
+      selectedDate.value = allParamsFromPreviousPage.selectedDate;
+      productTypeId.value = allParamsFromPreviousPage.productTypeId;
+      adultCount.value =
+        allParamsFromPreviousPage.adultCount == null
+          ? 0
+          : allParamsFromPreviousPage.adultCount;
+      childCount.value =
+        allParamsFromPreviousPage.childCount == null
+          ? 0
+          : allParamsFromPreviousPage.childCount;
+      infantCount.value =
+        allParamsFromPreviousPage.infantCount == null
+          ? 0
+          : allParamsFromPreviousPage.infantCount;
+      buyCount.value =
+        allParamsFromPreviousPage.buyCount == null
+          ? 0
+          : allParamsFromPreviousPage.buyCount;
+      totalPrice.value = allParamsFromPreviousPage.totalPrice;
+
+      console.log("productName.value");
+      console.log(productName.value);
+
+      initPeopleform();
+      contactform.value = {
+        name: "",
+        email: "",
+        phone: "",
+        address: "",
+      };
+    });
+
+    return {
+      productName,
+      packageCategory,
+      selectedDate,
+      adultCount,
+      childCount,
+      infantCount,
+      buyCount,
+      peopleform,
+      genderOption: [
+        { label: "Male", value: 1 },
+        { label: "Female", value: 0 },
+      ],
+      country,
+      contactform,
+      coupon,
+      totalPrice,
+      submitOrder,
+    };
+  },
+};
+</script>

+ 111 - 0
hichina-main-front-mobile-first-v2/src/pages/ContactPage.vue

@@ -0,0 +1,111 @@
+<template>
+  <q-page
+    ><div style="margion: 0 auto; min-height: 50px">
+      <div class="text-center text-h5">
+        You need help from our customer serivce?
+      </div>
+      <div class="text-center text-h6 text-subtitle text-grey-13">
+        Please send your demands to
+      </div>
+      <div class="text-center">
+        <a href="mailto:customerservice@hichinatrip.com"
+          >customerservice@hichinatrip.com</a
+        >
+      </div>
+    </div>
+    <div class="text-center text-subtitle1">
+      Our co-workers will will response ASAP
+    </div>
+    <div class="row justify-center q-mt-md">
+      <div class="col-6 text-center">
+        <q-card
+          class="trip"
+          style="max-width: 450px; display: inline-block"
+          @click="redirectToLink('https://wj.qq.com/s2/13001219/6136/')"
+        >
+          <q-card-section>
+            <q-img
+              src="https://assets.traveltriangle.com/blog/wp-content/uploads/2017/12/Travel-Plan.jpg"
+              alt="Image 1"
+            />
+          </q-card-section>
+          <q-card-section>
+            <div class="text-h6">Customize Your Trip</div>
+            <div>Plan your journey.</div>
+          </q-card-section>
+        </q-card>
+      </div>
+      <div class="col-6 text-center">
+        <q-card
+          class="ticket"
+          style="max-width: 450px; display: inline-block"
+          @click="
+            goPage('/product-detail/770ccc30-7369-4df5-83df-35977653219d')
+          "
+        >
+          <q-card-section>
+            <q-img
+              src="https://newblogimages.oss-cn-nanjing.aliyuncs.com/96cd118a-2f2f-4954-b629-c7f51a65493e.jpg?x-oss-process=style/blogcontent"
+              alt="Image 2"
+            />
+          </q-card-section>
+          <q-card-section>
+            <div class="text-h6">Book Flights</div>
+            <div>
+              Saves your time in flight search, price comparison, through our 1
+              on 1 VIP communication
+            </div>
+          </q-card-section>
+        </q-card>
+      </div>
+      <div>
+        <div class="text-center text-subtitle1">
+          Our co-workers will will response ASAP
+        </div>
+        <div class="text-center text-h5 text-grey-13">
+          Also, add us on Wechat/WhatsAPP!
+        </div>
+        <div class="text-center">
+          <q-img
+            style="width: 300px"
+            src="https://photoprism.hichinatravel.com/api/v1/t/d68ee79239386b14869f548a423de4bcc7e3cb31/32mcf2k4/fit_2048"
+            alt=""
+          />
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+export default {
+  name: "ContactPage",
+  methods: {
+    redirectToLink(link) {
+      window.open(link, "_blank");
+    },
+  },
+};
+</script>
+<style>
+.trip {
+  cursor: pointer;
+  width: 100%;
+  margin-bottom: 50px;
+  margin-top: 50px;
+  height: 450px;
+}
+
+.ticket {
+  cursor: pointer;
+  width: 100%;
+  margin-bottom: 50px;
+  margin-top: 50px;
+  height: 450px;
+}
+
+.q-img {
+  max-width: 100%;
+  height: auto;
+}
+</style>

+ 227 - 0
hichina-main-front-mobile-first-v2/src/pages/DestinationDetailPage.vue

@@ -0,0 +1,227 @@
+<template>
+  <q-page>
+    <div class="row" style="height: 110px; background-color: #e5f2fa">
+      <div class="col-12 col-sm-10 column" style="margin-left: 20px">
+        <div class="q-pt-md q-pl-md text-black text-weight-bold">
+          Home > Destination > {{ destinationName }}
+        </div>
+        <div class="col text-weight-bold q-pl-md text-h3">
+          {{ destinationName }}
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <!-- Left Column for Image -->
+      <div
+        class="col-12 col-sm-5 row q-px-md q-py-lg d-flex justify-center align-center"
+        style="height: 450px; background-color: white"
+      >
+        <q-img
+          :src="destinationProfileImage"
+          style="width: 100%; max-width: 650px; height: 100%"
+          fit="cover"
+        >
+        </q-img>
+      </div>
+
+      <!-- Right Column for Description, Guidebook, and Download Button -->
+      <div class="col-12 col-sm-7 row q-px-md q-py-lg" style="height: 450px">
+        <div
+          class="column col-12 d-flex flex-column justify-between"
+          style="background-color: #eff6fd; height: 100%"
+        >
+          <div class="q-pa-md text-subtitle1">
+            <div v-html="description"></div>
+          </div>
+          <div class="q-pa-md text-h4" style="margin-top: auto">
+            Guidebook of {{ destinationName }}
+            <q-btn class="primary" color="primary" @click="goDownload">
+              <q-icon left size="3em" name="download" />
+              <div>Download</div>
+            </q-btn>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div
+      class="row flex flex-left text-h4 text-weight-bold"
+      style="
+        background-color: #eff6fd;
+        border-top: 1px solid gray;
+        height: 55px;
+        margin-left: 30px;
+      "
+    >
+      Places to go in {{ destinationName }}
+    </div>
+
+    <div
+      v-for="item in childDestinations"
+      v-bind:key="item.destinationId"
+      class="row q-pa-md"
+      style="width: 90%; min-height: 200px; margin: 0 auto"
+    >
+      <div class="col-3 q-pa-md" style="border-right: 1px solid gray">
+        <q-img
+          class="cursor-pointer"
+          @click="goPage('/destination-detail/' + item.destinationId)"
+          fit="fill"
+          :src="normalizeMultiImageUrl(item.destinationProfileImage)"
+        ></q-img>
+      </div>
+      <div class="col-9 column q-pa-md">
+        <div class="text-h4 text-weight-bold" style="background-color: #f3f6f8">
+          <a :href="'./destination-detail/' + item.destinationId">{{
+            item.destinationName
+          }}</a>
+        </div>
+        <div class="q-pt-md">
+          {{ removeHtmlTag(item.description) }}
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance } from "vue";
+import { api } from "boot/axios";
+import { useRoute } from "vue-router";
+import { useSeoMeta } from "unhead";
+import { useQuasar } from "quasar";
+export default {
+  name: "DestinationDetailPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const route = useRoute();
+
+    const description = ref("");
+    const destinationProfileImage = ref("");
+    const downloadUrl = ref("");
+    const destinationName = ref("");
+    const childDestinations = ref([]);
+    const relevantToursProduct = ref([]);
+
+    function goDownload() {
+      if (downloadUrl.value == null || downloadUrl.value == "") {
+        gp.$generalNotify(
+          $q,
+          false,
+          "There is no guidebook for this destination"
+        );
+
+        return;
+      } else {
+        window.location.href = downloadUrl.value;
+      }
+    }
+
+    function logPv() {
+      api
+        .post(
+          "/api/public/pagestats/pv/destination-detail-" +
+            route.params.destinationId
+        )
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+    function logView() {
+      api
+        .post(
+          "/api/public/pagestats/view-destination/" + route.params.destinationId
+        )
+        .then((res) => {
+          console.log("view cnt of this destination:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    function loadRelatedTours() {
+      api
+        .get(
+          "/api/public/destination/relavanttourproduct/" +
+            route.params.destinationId
+        )
+        .then((response) => {
+          if (response.data.ok === true) {
+            relevantToursProduct.value = response.data.data;
+          } else {
+            relevantToursProduct.value = [];
+          }
+        })
+        .catch((e) => {
+          console.log("get relevant tours product err");
+          console.log(e);
+        });
+    }
+
+    function loadChildrenDestination() {
+      api
+        .get("/api/public/destination/children/" + route.params.destinationId)
+        .then((response) => {
+          childDestinations.value = response.data.data;
+        })
+        .catch((e) => {
+          console.log("get destination detail err");
+          console.log(e);
+        });
+    }
+
+    function loadDestinations() {
+      api
+        .get("/api/public/destination/" + route.params.destinationId)
+        .then((response) => {
+          description.value = response.data.data.description;
+          destinationProfileImage.value = gp.$normalizeMultiImageUrl(
+            response.data.data.destinationProfileImage
+          );
+          console.log(destinationProfileImage.value);
+          downloadUrl.value = response.data.data.downloadUrl;
+          destinationName.value = response.data.data.destinationName;
+          useSeoMeta({
+            title: destinationName.value,
+            description: description.value,
+            ogDescription: description.value,
+            ogTitle: destinationName.value,
+            ogImage:
+              "https://www.hichinatravel.com/static/png/name-67280b81.png",
+            twitterCard: "summary_large_image",
+          });
+        })
+        .catch((e) => {
+          console.log("get destination detail err");
+          console.log(e);
+        });
+    }
+
+    onMounted(() => {
+      logPv();
+      logView();
+      console.log("on mounted destination detail page");
+      loadDestinations();
+      loadChildrenDestination();
+      loadRelatedTours();
+    });
+    return {
+      destinationName,
+      destinationProfileImage,
+      description,
+      childDestinations,
+      goDownload,
+    };
+  },
+};
+</script>

+ 155 - 0
hichina-main-front-mobile-first-v2/src/pages/DestinationPage.vue

@@ -0,0 +1,155 @@
+<template>
+  <q-page>
+    <div class="row justify-center text-blue-6 text-h4 q-pt-xl">
+      {{ $t("seasonal_recommendation") }}
+    </div>
+    <div class="row justify-center q-mt-md">
+      <q-input
+        class="col-12 col-sm-8 col-md-6"
+        rounded
+        dense
+        outlined
+        v-model="query"
+        @update:model-value="(val) => updateQuery(val)"
+        :label="$t('search_by_title')"
+      />
+    </div>
+    <div class="row justify-left">
+      <div
+        class="col-12 col-sm-4 col-md-3"
+        v-for="item in globalDestinationCards"
+        :key="item.destinationId"
+      >
+        <div class="q-pa-md">
+          <q-card
+            class="destination-card cursor-pointer rounded-borders"
+            @mouseenter="hoverFlag = true"
+            @mouseleave="hoverFlag = true"
+          >
+            <q-img
+              @click="goPage('/destination-detail/' + item.destinationId)"
+              class="rounded-borders"
+              style="height: 100%"
+              :src="normalizeMultiImageUrl(item.destinationProfileImage)"
+            >
+              <div class="absolute-bottom">
+                <div class="text-h6 text-white">
+                  <a
+                    style="color: inherit"
+                    :href="'./destination-detail/' + item.destinationId"
+                    >{{ item.destinationName }}</a
+                  >
+                </div>
+                <div class="text-subtitle2">
+                  {{ item.parentDestinationName }}
+                </div>
+              </div>
+            </q-img>
+          </q-card>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-12">
+        <p class="text-center" style="background-color: #b4b4b4">
+          Scroll to load more
+        </p>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { api } from "boot/axios";
+import { debounce } from "lodash";
+export default {
+  name: "DestinationPage",
+  setup() {
+    const pageSize = ref(44);
+    const currentPage = ref(1);
+    const destinationCards = ref([]);
+    const globalDestinationCards = ref([]);
+    const totalDestinationCount = ref(-1);
+    const query = ref("");
+    const hoverFlag = ref(true);
+
+    function loadMore() {
+      currentPage.value += 1;
+
+      var maxPage = totalDestinationCount.value / pageSize.value;
+      if (currentPage.value <= maxPage + 1) {
+        loadDestinations();
+      }
+    }
+
+    function getNextBatch() {
+      window.onscroll = debounce(function () {
+        let bottomOfWindow =
+          document.documentElement.scrollTop + window.innerHeight + 100 >
+          document.documentElement.scrollHeight;
+
+        if (bottomOfWindow) {
+          loadMore();
+        }
+      }, 500);
+    }
+
+    const updateQuery = debounce((value) => {
+      globalDestinationCards.value = [];
+      currentPage.value = 1;
+      loadDestinations();
+    }, 500);
+
+    function loadDestinations() {
+      destinationCards.value = [];
+      var params = {};
+      params.pageSize = pageSize.value;
+      console.log("loading page: " + currentPage.value);
+      params.page = currentPage.value;
+      params.query = query.value;
+      api
+        .get("/api/public/destination/list", { params: params })
+        .then(function (response) {
+          destinationCards.value = response.data.data.data;
+          totalDestinationCount.value = response.data.data.total;
+          globalDestinationCards.value = globalDestinationCards.value.concat(
+            destinationCards.value
+          );
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+    function logPv() {
+      api
+        .post("/api/public/pagestats/pv/destination")
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+    onMounted(() => {
+      logPv();
+      loadDestinations();
+      getNextBatch();
+    });
+
+    return {
+      query,
+      globalDestinationCards,
+      hoverFlag,
+      updateQuery,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.destination-card
+  width: 100%
+  max-width: 550px
+  height: 300px
+</style>

+ 31 - 0
hichina-main-front-mobile-first-v2/src/pages/ErrorNotFound.vue

@@ -0,0 +1,31 @@
+<template>
+  <div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
+    <div>
+      <div style="font-size: 30vh">
+        404
+      </div>
+
+      <div class="text-h2" style="opacity:.4">
+        Oops. Nothing here...
+      </div>
+
+      <q-btn
+        class="q-mt-xl"
+        color="white"
+        text-color="blue"
+        unelevated
+        to="/"
+        label="Go Home"
+        no-caps
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'ErrorNotFound'
+})
+</script>

+ 52 - 0
hichina-main-front-mobile-first-v2/src/pages/FinishpayPage.vue

@@ -0,0 +1,52 @@
+<template>
+  <q-page class="flex flex-center">
+    Redirecting... <vue-json-pretty :data="route.query" />
+  </q-page>
+</template>
+
+<script>
+import { useQuasar } from "quasar";
+import { onMounted, getCurrentInstance } from "vue";
+import { useRoute } from "vue-router";
+import { api } from "boot/axios";
+import VueJsonPretty from "vue-json-pretty";
+import "vue-json-pretty/lib/styles.css";
+export default {
+  name: "FinishpayPage",
+  components: {
+    VueJsonPretty: VueJsonPretty,
+  },
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const route = useRoute();
+    onMounted(() => {
+      if (
+        route.query.out_trade_no != null &&
+        route.query.out_trade_no.length > 0
+      ) {
+        var params = {};
+        params.tradeNo = route.query.trade_no;
+        params.method = route.query.method;
+        api
+          .put("/api/v1/order/succeed/" + route.query.out_trade_no, params)
+          .then((res) => {
+            gp.$generalNotify($q, true, "Succeed finishing pay");
+            var params2pass = {};
+            params2pass.active = 2;
+            gp.$goPage("/my-orders");
+          })
+          .catch((err) => {
+            gp.$generalNotify($q, false, "Fail creating order" + err);
+          });
+      }
+    });
+    return {
+      route,
+    };
+  },
+};
+</script>

+ 149 - 0
hichina-main-front-mobile-first-v2/src/pages/GuideIntroPage.vue

@@ -0,0 +1,149 @@
+<template>
+  <q-page>
+    <div class="q-pa-md">
+      <q-carousel
+        animated
+        v-model="slide"
+        navigation
+        infinite
+        height="380px"
+        :autoplay="2000"
+        arrows
+        transition-prev="slide-right"
+        transition-next="slide-left"
+        @mouseenter="autoplay = false"
+        @mouseleave="autoplay = true"
+      >
+        <q-carousel-slide
+          v-for="(item, index) in sliders"
+          :key="index"
+          :name="index"
+          :img-src="item.imageUrl"
+          class="cursor-pointer"
+          @click="goToBlog(item.linkTo)"
+        >
+        </q-carousel-slide>
+      </q-carousel>
+    </div>
+    <div class="row q-pa-md">
+      <div class="q-gutter-y-md col-12 col-md-6">
+        <q-tabs v-model="tab" dense align="justify" class="text-primary">
+          <q-tab :ripple="false" name="lg" :label="$t('latest_guidebooks')" />
+          <q-tab
+            :ripple="false"
+            name="mdg"
+            :label="$t('most_downloaded_guidebooks')"
+          />
+        </q-tabs>
+      </div>
+    </div>
+    <div class="row justify-left">
+      <div
+        class="col-12 col-sm-4 col-md-2"
+        v-for="item in guidebooks"
+        :key="item.guideId"
+      >
+        <div class="q-pa-md">
+          <q-card
+            @click="gotoUrl(item.downloadUrl)"
+            class="guidebook-card cursor-pointer rounded-borders"
+            @mouseenter="hoverFlag = true"
+            @mouseleave="hoverFlag = false"
+          >
+            <q-img
+              class="rounded-borders"
+              style="height: 100%"
+              :src="item.coverImageUrl"
+            >
+              <div class="absolute-bottom">
+                <div class="text-h6">{{ item.destinationName }}</div>
+                <div class="text-subtitle2">
+                  {{ item.parentDestinationName }}
+                </div>
+              </div>
+            </q-img>
+          </q-card>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { api } from "boot/axios";
+export default {
+  name: "GuideIntroPage",
+  setup() {
+    const sliders = ref([]);
+    const pageSize = ref(1000);
+    const currentPage = ref(1);
+    const guidebooks = ref([]);
+
+    function loadSliders() {
+      api
+        .get("/api/public/pagecontent/guidebookintrosliders")
+        .then(function (response) {
+          console.log("guidebookintrosliders:");
+          console.log(response.data.data);
+          sliders.value = response.data.data;
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadGuideBooks() {
+      console.log("loading latest guidebooks...");
+      var params = {};
+      params.pageSize = pageSize.value;
+      params.page = currentPage.value;
+      (params.query = ""),
+        api
+          .get("/api/public/guidebook", {
+            params: params,
+          })
+          .then(function (response) {
+            console.log("all guidebook list:");
+            guidebooks.value = response.data.data.data;
+            console.log(guidebooks.value);
+          })
+          .catch(function (error) {
+            console.log(error);
+          });
+    }
+
+    function logPv() {
+      api
+        .post("/api/public/pagestats/pv/guideintro")
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    onMounted(() => {
+      logPv();
+      loadSliders();
+      loadGuideBooks();
+    });
+
+    return {
+      hoverFlag: ref(true),
+      slide: ref(1),
+      sliders,
+      tab: ref("lg"),
+      guidebooks,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.guidebook-card
+  width: 100%
+  max-width: 550px
+  height: 300px
+</style>

+ 399 - 0
hichina-main-front-mobile-first-v2/src/pages/IndexPage.vue

@@ -0,0 +1,399 @@
+<template>
+  <q-page>
+    <div class="q-pa-md">
+      <q-carousel
+        animated
+        v-model="slide"
+        navigation
+        infinite
+        height="540px"
+        :autoplay="2000"
+        arrows
+        transition-prev="slide-right"
+        transition-next="slide-left"
+        @mouseenter="autoplay = false"
+        @mouseleave="autoplay = true"
+      >
+        <q-carousel-slide
+          v-for="(item, index) in sliders"
+          :key="index"
+          :name="index"
+          :img-src="item.imageUrl"
+          class="cursor-pointer"
+          @click="gotoUrl(item.linkToBlog)"
+        >
+          <div class="q-mt-xl q-ml-xl absolute-left custom-caption">
+            <div style="height: 100px"></div>
+            <div class="text-h1 text-weight-bolder">{{ item.title }}</div>
+            <div class="text-h5 text-weight-medium">{{ item.subTitle }}</div>
+          </div>
+        </q-carousel-slide>
+      </q-carousel>
+    </div>
+    <div class="q-pa-md" style="height: 40px"></div>
+    <div class="row">
+      <div class="col-12 col-md-6 text-blue-6 text-h4 q-pl-md">
+        {{ $t("destinations") }}
+      </div>
+      <div class="col-12 col-md-6 text-right">
+        <div @click="goPage('/destination')" class="text-blue-6 cursor-pointer">
+          {{ $t("more_destinations") }} >
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="text-h3 q-pl-md q-mt-md">
+        {{ $t("places_in_china") + "!" }}
+      </div>
+    </div>
+
+    <div class="row justify-center">
+      <div
+        class="col-12 col-sm-4 col-md-2"
+        v-for="item in randDestinations"
+        :key="item.blogId"
+      >
+        <div class="q-pa-md">
+          <q-card
+            @click="goPage('/destination-detail/' + item.destinationId)"
+            class="rand-destination-card cursor-pointer rounded-borders"
+            @mouseenter="hoverFlag = true"
+            @mouseleave="hoverFlag = false"
+          >
+            <q-img
+              class="rounded-borders"
+              style="height: 100%"
+              :src="normalizeMultiImageUrl(item.destinationProfileImage)"
+            >
+              <div class="absolute-bottom" v-if="hoverFlag">
+                <div class="text-h6">{{ item.destinationName }}</div>
+                <div class="text-subtitle2">
+                  {{ item.parentDestinationName }}
+                </div>
+              </div>
+            </q-img>
+          </q-card>
+        </div>
+      </div>
+    </div>
+    <div class="q-pa-md">
+      <div class="row">
+        <div
+          v-if="$q.screen.gt.xs"
+          @click="goPage('/contact')"
+          class="cursor-pointer col-12 q-mt-md q-pt-md rounded-borders text-white text-weight-bold text-h5 text-no-wrap text-center"
+          style="
+            background-color: #2a82e4;
+            height: 80px;
+            border-radius: 20px;
+            border: 1px solid black;
+          "
+        >
+          {{ $t("tailor_made_trip") }}
+        </div>
+        <div
+          v-if="!$q.screen.gt.xs"
+          @click="goPage('/contact')"
+          class="cursor-pointer col-12 q-mt-md q-pt-md rounded-borders text-white text-weight-bold text-subtitle1 text-no-wrap text-center"
+          style="
+            background-color: #2a82e4;
+            height: 80px;
+            border-radius: 20px;
+            border: 1px solid black;
+          "
+        >
+          Want a Tailor Made Trip to China? Click Here for Help
+        </div>
+      </div>
+    </div>
+    <div class="q-pa-md">
+      <div class="row">
+        <div
+          class="col-12 cursor-pointer"
+          style="height: 160px"
+          @click="goPage('/contact')"
+        >
+          <q-img :src="homePostImageUrl" fit="fill" style="height: 100%">
+          </q-img>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="text-h5 text-weight-bold text-blue-6 q-pl-md q-mt-md">
+        {{ $t("people_are_traveling") + "!" }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="text-h3 text-weight-medium q-pl-md q-mt-md">
+        {{ $t("whats_in_china") + "!" }}
+      </div>
+    </div>
+    <div class="row justify-left">
+      <div
+        class="col-12 col-sm-4 col-md-2"
+        v-for="(item, index) in globalUnifiedItemList"
+        v-bind:key="index"
+      >
+        <div class="q-pa-md">
+          <q-card
+            v-if="item.type === 'blog'"
+            class="cursor-pointer"
+            flat
+            bordered
+            style="height: 530px"
+          >
+            <q-img
+              @click="goPage('/blog-detail/' + item.value.blogId)"
+              :src="item.value.headImageUrl"
+              style="height: 300px"
+              placeholder-src="https://photoprism.hichinatravel.com/api/v1/t/2bfc32550ae040956f7e861566d26c487c0143e7/32mcf2k4/tile_224"
+            />
+
+            <q-card-section style="max-height: 200px; overflow: hidden">
+              <div class="text-overline text-orange-9">
+                {{ item.value.createdTime }}
+              </div>
+              <div class="text-h5 q-mt-sm q-mb-xs">
+                <a :href="'./blog-detail/' + item.value.blogId">{{
+                  item.value.title
+                }}</a>
+              </div>
+              <div class="text-caption text-grey">
+                {{ item.value.content }}
+              </div>
+            </q-card-section>
+          </q-card>
+          <q-card
+            v-if="item.type === 'product'"
+            class="cursor-pointer"
+            flat
+            bordered
+            style="height: 400px"
+          >
+            <q-img
+              @click="goPage('/product-detail/' + item.value.skuGroupId)"
+              :src="item.value.imageUrl"
+              style="height: 220px"
+              placeholder-src="https://photoprism.hichinatravel.com/api/v1/t/2bfc32550ae040956f7e861566d26c487c0143e7/32mcf2k4/tile_224"
+            />
+
+            <q-card-section>
+              <div class="text-subtitle1 text-pink">
+                Start from ¥{{ item.value.minPrice }}
+              </div>
+              <div class="text-h5 q-mt-sm q-mb-xs">
+                {{ item.value.skuGroupName }}
+              </div>
+            </q-card-section>
+          </q-card>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-12">
+        <p class="text-center" style="background-color: #b4b4b4">
+          Scroll to load more
+        </p>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { defineComponent } from "vue";
+import { debounce } from "lodash";
+import { api } from "boot/axios";
+
+export default defineComponent({
+  name: "IndexPage",
+  setup() {
+    const sliders = ref([]);
+    const randDestinations = ref([]);
+    const hoverFlag = ref(false);
+    const homePostLink = ref("");
+    const homePostImageUrl = ref("");
+
+    const currentPage = ref(1);
+    const totalBlogCount = ref(-1);
+    const blogPageSize = ref(30);
+    const productPageSizePage = ref(5);
+    const loading = ref(false);
+
+    const bloglist = ref([]);
+    const productlist = ref([]);
+
+    const globalUnifiedItemList = ref([]);
+    const unifiedItemList = ref([]);
+
+    // function goToBlog(url) {
+    //   window.location.href = url;
+    // }
+
+    // function normalizeMultiImageUrl(input) {
+    //   if (input.indexOf(",") > -1) {
+    //     return input.split(",").shift();
+    //   } else if (input.indexOf(";") > -1) {
+    //     return input.split(";").shift();
+    //   }
+    //   return input;
+    // }
+
+    function loadRand6Destinations() {
+      api
+        .get("/api/public/destination/rand6")
+        .then((response) => {
+          randDestinations.value = response.data.data;
+        })
+        .catch((e) => {
+          alert(e);
+        });
+    }
+
+    function loadHomeSliders() {
+      api
+        .get("/api/public/pagecontent/homesliders")
+        .then((response) => {
+          sliders.value = response.data.data;
+          loadBlogList();
+        })
+        .catch((e) => {
+          console.log(e);
+        });
+    }
+
+    function loadHomePost() {
+      api
+        .get("/api/public/pagecontent/homepost")
+        .then((response) => {
+          homePostLink.value = response.data.data.postLink;
+          homePostImageUrl.value = response.data.data.postImageUrl;
+        })
+        .catch((e) => {
+          console.log(e);
+        });
+    }
+
+    function loadProducts() {
+      var params = {};
+      params.pageSize = productPageSizePage.value;
+      params.query = "";
+      params.page = currentPage.value;
+      params.productTypeId = "";
+      api
+        .get("/api/public/productsku/productskugrouplist", { params: params })
+        .then(function (response) {
+          productlist.value = response.data.data.data;
+
+          for (var index in productlist.value) {
+            var obj = {};
+            obj.type = "product";
+            obj.value = productlist.value[index];
+            unifiedItemList.value.push(obj);
+          }
+
+          globalUnifiedItemList.value = globalUnifiedItemList.value.concat(
+            unifiedItemList.value
+          );
+
+          loading.value = false;
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadBlogList() {
+      loading.value = true;
+      unifiedItemList.value = [];
+      var params = {};
+      params.pageSize = blogPageSize.value;
+      params.query = "";
+      console.log("loading page: " + currentPage.value);
+      params.page = currentPage.value;
+      api
+        .get("/api/public/blog/list", { params: params })
+        .then(function (response) {
+          if (totalBlogCount.value == -1) {
+            totalBlogCount.value = response.data.data.total;
+          }
+          bloglist.value = response.data.data.data;
+          for (var index in bloglist.value) {
+            var obj = {};
+            // obj.type=parseInt(Math.random() * 2)==0?'blog':'scaleblog';
+            obj.type = "blog";
+            obj.value = bloglist.value[index];
+            unifiedItemList.value.push(obj);
+          }
+          loadProducts();
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function loadMore() {
+      currentPage.value += 1;
+      var maxPage = totalBlogCount.value / blogPageSize.value;
+      if (currentPage.value <= maxPage + 1) {
+        loadBlogList();
+      }
+    }
+
+    function getNextBatch() {
+      window.onscroll = debounce(function () {
+        let bottomOfWindow =
+          document.documentElement.scrollTop + window.innerHeight + 100 >
+          document.documentElement.scrollHeight;
+
+        if (bottomOfWindow) {
+          loadMore();
+        }
+      }, 500);
+    }
+
+    function logPv() {
+      api
+        .post("/api/public/pagestats/pv/home")
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    onMounted(() => {
+      logPv();
+      // we call "next()" method of our component
+      loadHomeSliders();
+      loadRand6Destinations();
+      loadHomePost();
+      getNextBatch();
+    });
+
+    return {
+      slide: ref(0),
+      autoplay: ref(true),
+      sliders,
+      randDestinations,
+      hoverFlag,
+      homePostLink,
+      homePostImageUrl,
+      globalUnifiedItemList,
+    };
+  },
+});
+</script>
+<style lang="sass" scoped>
+.custom-caption
+  text-align: left
+  padding: 12px
+  color: white
+
+.rand-destination-card
+  width: 100%
+  max-width: 550px
+  height: 300px
+</style>

+ 281 - 0
hichina-main-front-mobile-first-v2/src/pages/LoginPage.vue

@@ -0,0 +1,281 @@
+<template>
+  <q-page>
+    <div style="height: 82px"></div>
+    <div class="row q-mb-xl justify-center">
+      <div
+        class="col-10 col-sm-5 col-md-3 rounded-borders login-border shadow-7"
+      >
+        <div class="text-h5 text-left text-weight-bold text-black q-pa-md">
+          {{ $t("login") }}
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-input
+            :rules="[(val) => !!val || 'Field is required']"
+            color="blue-12"
+            v-model="username"
+            :label="$t('enter_email')"
+            ref="usernameInput"
+          >
+            <template v-slot:prepend>
+              <q-icon name="account_circle" />
+            </template>
+          </q-input>
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-input
+            :rules="[(val) => !!val || 'Field is required']"
+            color="blue-12"
+            v-model="password"
+            type="password"
+            :label="$t('enter_password')"
+            ref="passwordInput"
+          >
+            <template v-slot:prepend>
+              <q-icon name="lock" />
+            </template>
+          </q-input>
+        </div>
+        <div class="col-12 q-pa-md d-flex text-center">
+          <q-btn
+            align="between"
+            class="btn-fixed-width"
+            color="blue"
+            icon="login"
+            @click="login()"
+            :label="$t('login')"
+          />
+        </div>
+        <div
+          style="border-top: 1px solid rgb(202, 202, 202); margin: 20px"
+        ></div>
+        <div class="col-12 q-pa-md d-flex text-center">
+          <q-btn
+            align="between"
+            class="btn-fixed-width"
+            color="primary"
+            label="Continue with Facebook"
+            icon="facebook"
+            @click="goToFacebookLogin"
+          />
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, onBeforeMount, getCurrentInstance } from "vue";
+import { useQuasar } from "quasar";
+import { useI18n } from "vue-i18n";
+import { api } from "boot/axios";
+import Qs from "qs";
+export default {
+  name: "LoginPage",
+  setup() {
+    const { locale, $t } = useI18n();
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const username = ref("");
+    const password = ref("");
+
+    function login() {
+      const isValidPassword = instance.refs.passwordInput.validate();
+      const isValidUsername = instance.refs.usernameInput.validate();
+      if (isValidUsername && isValidPassword) {
+        // do real login logic
+        var data = {
+          username: username.value,
+          password: password.value,
+        };
+
+        const updateLoginTypeParams = {};
+        updateLoginTypeParams.email = username.value;
+        updateLoginTypeParams.loginType = "regular";
+
+        gp.$showLoading($q);
+        api
+          .put("/api/public/login/type", updateLoginTypeParams)
+          .then((res) => {
+            // if succeed updating login type to regular
+            if (res.data.ok == true) {
+              // could succeed updating login type,  do real login
+              api
+                .post("/login", Qs.stringify(data), {
+                  headers: {
+                    "Content-Type": "application/x-www-form-urlencoded",
+                  },
+                })
+                .then((response) => {
+                  gp.$generalNotify($q, true, "Login succeed!");
+                  location.reload();
+                })
+                .catch((e) => {
+                  gp.$generalNotify($q, false, "Fail login error ");
+                  gp.$hideLoading($q);
+                  console.log("Fail login error message: ");
+                  console.log(e);
+                });
+            } else {
+              // could fail due to user not exist
+              gp.$generalNotify($q, false, res.data.message);
+              gp.$hideLoading($q);
+            }
+          })
+          .catch((err) => {
+            // could fail for unknown reason
+            gp.$generalNotify($q, false, "Fail updating login type");
+            console.log("Fail updating login type err:");
+            console.log(err);
+            gp.$hideLoading($q);
+          });
+      } else {
+        gp.$generalNotify($q, false, "Not valid input");
+      }
+    }
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in setup: " + response.data);
+          gp.$goPage("/");
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+        });
+    }
+
+    function goToFacebookLogin() {
+      FB.login(
+        function (response) {
+          // handle the response
+          if (response.authResponse) {
+            var accessToken = response.authResponse.accessToken;
+
+            // get my info and set
+            FB.api(
+              "/me",
+              "GET",
+              { fields: "id,name,email,picture" },
+              function (response) {
+                // try login using the oauth way
+                const params = {};
+                params.accessToken = accessToken;
+                params.facebookId = response.id;
+                params.email = response.email;
+                params.name = response.name;
+                params.profileImageUrl = response.picture.data.url;
+
+                var emailStore = response.email;
+                api
+                  .post("/api/public/login/prereg-facebook", params)
+                  .then((res) => {
+                    var data = {
+                      username: emailStore,
+                      password: accessToken,
+                    };
+
+                    gp.$showLoading($q);
+                    api
+                      .post("/login", Qs.stringify(data), {
+                        headers: {
+                          "Content-Type": "application/x-www-form-urlencoded",
+                        },
+                      })
+                      .then((response) => {
+                        gp.$hideLoading($q);
+                        location.reload();
+                      })
+                      .catch((e) => {
+                        gp.$hideLoading($q);
+                        gp.$generalNotify($q, false, "Error message: " + e);
+                      });
+                  })
+                  .catch((err) => {
+                    gp.$hideLoading(false);
+                    gp.$generalNotify(
+                      $q,
+                      false,
+                      "Fail pre register facebook user"
+                    );
+                    console.log("Fail pre register facebook user");
+                    console.log(err);
+                  });
+              }
+            );
+          } else {
+            console.log("User cancelled login or did not fully authorize.");
+            gp.$generalNotify(
+              $q,
+              false,
+              "User cancelled login or did not fully authorize."
+            );
+          }
+        },
+        { scope: "email" }
+      );
+    }
+
+    function initFacebookSDK() {
+      // Load the Facebook SDK asynchronously
+      const loadFacebookSDK = new Promise((resolve) => {
+        window.fbAsyncInit = function () {
+          FB.init({
+            appId: "854574772677522",
+            autoLogAppEvents: true,
+            xfbml: true,
+            version: "v13.0",
+          });
+          resolve();
+        };
+      });
+
+      // Load the SDK script
+      (function (d, s, id) {
+        var js,
+          fjs = d.getElementsByTagName(s)[0];
+        console.log("js");
+        console.log(js);
+        console.log("fjs");
+        console.log(fjs);
+        if (d.getElementById(id)) {
+          return;
+        }
+        js = d.createElement(s);
+        js.id = id;
+        js.src = "https://connect.facebook.net/en_US/sdk.js";
+        fjs.parentNode.insertBefore(js, fjs);
+      })(document, "script", "facebook-jssdk");
+
+      // Wait for the SDK to be loaded
+      loadFacebookSDK.then(() => {
+        console.log("facebook sdk loaded");
+        // You can now use the Facebook SDK methods here
+        // For example, authenticate the user, fetch user data, etc.
+      });
+    }
+
+    onMounted(() => {
+      whoami();
+    });
+
+    onBeforeMount(() => {
+      initFacebookSDK();
+    });
+
+    return {
+      login,
+      goToFacebookLogin,
+      username,
+      password,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped>
+.login-border
+  border-radius: 10px
+</style>

+ 201 - 0
hichina-main-front-mobile-first-v2/src/pages/MyBlogsPage.vue

@@ -0,0 +1,201 @@
+<template>
+  <q-page>
+    <q-dialog v-model="confirmDelete" persistent>
+      <q-card>
+        <q-card-section class="row items-center">
+          <q-avatar icon="delete" color="primary" text-color="white" />
+          <span class="q-ml-sm"
+            >Are you sure you would like to delete this blog?.</span
+          >
+        </q-card-section>
+
+        <q-card-actions align="right">
+          <q-btn flat label="Cancel" color="primary" v-close-popup />
+          <q-btn
+            flat
+            label="Confirm Delete"
+            @click="deleteBLog"
+            color="primary"
+            v-close-popup
+          />
+        </q-card-actions>
+      </q-card>
+    </q-dialog>
+    <div class="row justify-center">
+      <div class="col-10 row">
+        <div class="col-12 text-h5 q-pa-md text-weight-bold text-center">
+          My Blog List
+        </div>
+        <div
+          class="q-mt-xl row col-12"
+          v-for="(item, index) in bloglist"
+          :key="index"
+        >
+          Created: {{ item.createdTime }}
+          <div class="col-12 row" style="border: 1px solid gray">
+            <div class="col-6 text-h6" style="border: 1px solid gray">
+              <a :href="'./blog-detail/' + item.blogId">{{ item.title }}</a>
+            </div>
+            <div class="col-1" style="border: 1px solid gray; min-width: 70px">
+              {{ item.draft ? "Draft" : "Published" }}
+            </div>
+            <div
+              class="col-5 row no-wrap justify-center"
+              style="border: 1px solid gray"
+            >
+              <q-btn
+                v-if="item.draft"
+                rounded
+                dense
+                color="primary"
+                label="Publish"
+                icon="publish"
+                @click="publishBlog(item.blogId)"
+              />
+              <q-btn
+                rounded
+                dense
+                color="primary"
+                icon="edit"
+                @click="goPage('/blog-edit/' + item.blogId)"
+              />
+              <q-btn
+                rounded
+                dense
+                color="red"
+                icon="delete"
+                @click="confirmDeleteBlog(item.blogId)"
+              />
+            </div>
+          </div>
+        </div>
+        <div class="q-pa-lg flex flex-center">
+          <q-pagination
+            v-model="currentPage"
+            @update:model-value="changePage"
+            :max="totalPages"
+          />
+        </div>
+      </div></div
+  ></q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance } from "vue";
+import { useQuasar } from "quasar";
+import { api } from "boot/axios";
+export default {
+  name: "MyBlogsPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const pageSize = ref(100);
+    const bloglist = ref([]);
+    const totalCnt = ref(0);
+    const totalPages = ref(1);
+    const currentPage = ref(1);
+    const confirmDelete = ref(false);
+    const todeleteBlogID = ref("");
+
+    function publishBlog(blogId) {
+      api
+        .put("/api/v1/blog/publish/" + blogId)
+        .then((res) => {
+          gp.$generalNotify($q, true, "Succeed publishing blog");
+          loadMyBlogs();
+        })
+        .catch((err) => {
+          gp.$generalNotify($q, false, "Fail publishing blog" + err);
+        });
+    }
+
+    function changePage() {
+      console.log("changing to page: ");
+      console.log(currentPage.value);
+      loadMyBlogs();
+    }
+
+    function confirmDeleteBlog(blogId) {
+      confirmDelete.value = true;
+      todeleteBlogID.value = blogId;
+    }
+
+    function deleteBLog() {
+      api
+        .delete("/api/v1/blog/" + todeleteBlogID.value)
+        .then((res) => {
+          gp.$generalNotify($q, true, "Succeed deleting blog");
+          loadMyBlogs();
+        })
+        .catch((err) => {
+          gp.$generalNotify($q, false, "Fail deleting blog" + err);
+        });
+    }
+
+    function loadMyBlogs() {
+      var params = {};
+      params.pageSize = pageSize.value;
+      params.page = currentPage.value;
+      params.query = "";
+      gp.$showLoading($q);
+      api
+        .get("/api/v1/blog/mybloglist", {
+          params: params,
+        })
+        .then(function (response) {
+          console.log("Got my blogs summary: ");
+          console.log(response.data.data);
+          bloglist.value = response.data.data.data;
+          totalCnt.value = response.data.data.total;
+          pageSize.value = response.data.data.pageSize;
+          currentPage.value = response.data.data.currentPage;
+
+          let divFloor = Math.floor(totalCnt.value / pageSize.value);
+          if (totalCnt.value % pageSize.value > 0) {
+            divFloor += 1;
+          }
+          totalPages.value = divFloor;
+          gp.$hideLoading($q);
+        })
+        .catch(function (error) {
+          console.log("failed to load my blogs: " + error);
+          gp.$hideLoading($q);
+        });
+    }
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in user page: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+
+    onMounted(() => {
+      whoami();
+      loadMyBlogs();
+    });
+
+    return {
+      pageSize,
+      bloglist,
+      totalCnt,
+      currentPage,
+      totalPages,
+      confirmDelete,
+      todeleteBlogID,
+      deleteBLog,
+      publishBlog,
+      confirmDeleteBlog,
+      changePage,
+    };
+  },
+};
+</script>

+ 110 - 0
hichina-main-front-mobile-first-v2/src/pages/MyOrdersPage.vue

@@ -0,0 +1,110 @@
+<template>
+  <q-page>
+    <div class="row justify-center">
+      <div class="col-12 col-sm-7 q-pa-md">
+        <div class="row col-12" style="border: 1px solid gray">
+          <div class="col-2">Product Name</div>
+          <div class="col-2">Package Category</div>
+          <div class="col-2">Start Date</div>
+          <div class="col-2">Total Price</div>
+          <div class="col-2">Payment Status</div>
+          <div class="col-2"></div>
+        </div>
+        <div v-for="(item, index) in orderSummaries" v-bind:key="index">
+          <div>
+            <div class="text-body1" style="background-color: #d6d6d6">
+              Order No. {{ item.orderId }} | Created On: {{ item.createdTime }}
+            </div>
+            <div
+              class="row col-12 text-subtitle1"
+              style="border: 1px solid gray"
+            >
+              <div class="col-2">{{ item.productName }}</div>
+              <div class="col-2">{{ item.packageCategory }}</div>
+              <div class="col-2">{{ item.selectedDate }}</div>
+              <div class="col-2">CNY: {{ item.totalPrice }}</div>
+              <div class="col-2">{{ item.orderStatus }}</div>
+              <div class="col-2">
+                <q-btn
+                  @click="goPage('/order-detail/' + item.orderId)"
+                  color="grey-4"
+                  text-color="primary"
+                  glossy
+                  dense
+                  unelevated
+                  icon="info"
+                  label="Order Details"
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance, reactive } from "vue";
+import { api } from "boot/axios";
+import { useQuasar } from "quasar";
+
+export default {
+  name: "MyOrdersPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const pageSize = ref(100);
+    const currentPage = ref(1);
+    const orderSummaries = ref([]);
+    const totalCnt = ref(0);
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in user page: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.goPage("/");
+        });
+    }
+
+    function loadMyOrderByPage() {
+      var params = {};
+      params.pageSize = pageSize.value;
+      params.page = currentPage.value;
+      params.query = "";
+      api
+        .get("/api/v1/order/myorders", {
+          params: params,
+        })
+        .then(function (response) {
+          console.log("Got my orders summary: ");
+          console.log(response.data.data);
+          orderSummaries.value = response.data.data.data;
+          totalCnt.value = response.data.data.total;
+          pageSize.value = response.data.data.pageSize;
+          currentPage.value = response.data.data.currentPage;
+        })
+        .catch(function (error) {
+          console.log("currently not logged in my order page: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+
+    onMounted(() => {
+      whoami();
+      loadMyOrderByPage();
+    });
+
+    return {
+      orderSummaries,
+    };
+  },
+};
+</script>

+ 243 - 0
hichina-main-front-mobile-first-v2/src/pages/OrderDetailPage.vue

@@ -0,0 +1,243 @@
+<template>
+  <q-page>
+    <div class="row justify-center">
+      <div class="col-7 q-pa-md">
+        <div>
+          <q-btn
+            color="primary"
+            @click="goPage('/my-orders')"
+            glossy
+            label="Return to order list"
+          />
+        </div>
+        <div class="q-my-md col-7 text-h5 text-weight-bold">Product Info</div>
+
+        <div class="row col-12 q-pa-md" style="background-color: #e7e7e7">
+          <div class="col-3">Product Name</div>
+          <div class="col-3">Package Category</div>
+          <div class="col-2">Total Price</div>
+          <div class="col-2">Payment Status</div>
+          <div class="col-2">Start Time</div>
+        </div>
+        <div class="row col-12 q-pa-md" style="border: 1px solid gray">
+          <div class="col-3">
+            <a :href="frontendBase + '/product-detail/' + skuGroupId">{{
+              productName
+            }}</a>
+          </div>
+          <div class="col-3">{{ packageCategory }}</div>
+          <div class="col-2">CNY:{{ totalPrice }}</div>
+          <div class="col-2">{{ orderStatus }}</div>
+          <div class="col-2">{{ selectedDate }}</div>
+        </div>
+      </div>
+      <div class="row col-7 q-pa-md">
+        <div class="text-h5 text-weight-bold">Passenger Info</div>
+        <div v-if="passengerInfo != null && passengerInfo.length > 0">
+          <q-table
+            ref="passengerInfoTableRef"
+            :rows="passengerInfo"
+            :columns="columns"
+            :separator="'cell'"
+          >
+            <template v-slot:body-cell-gender="props">
+              <q-td :props="props">
+                {{ props.row.gender == 1 ? "Male" : "Female" }}
+              </q-td>
+            </template>
+          </q-table>
+        </div>
+      </div>
+      <div class="row col-7 q-pa-md">
+        <div class="text-h5 text-weight-bold">Contact Info</div>
+        <div class="row col-12 q-pa-md" style="background-color: #e7e7e7">
+          <div class="col-3">Contact Person</div>
+          <div class="col-3">Email</div>
+          <div class="col-3">Phone</div>
+          <div class="col-3">Address</div>
+        </div>
+        <div class="row col-12" style="border: 1px solid gray">
+          <div class="col-3">{{ contactInfo.name }}</div>
+          <div class="col-3">{{ contactInfo.email }}</div>
+          <div class="col-3">{{ contactInfo.phone }}</div>
+          <div class="col-3">{{ contactInfo.address }}</div>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance, reactive } from "vue";
+import { api } from "boot/axios";
+import { useQuasar } from "quasar";
+import { useRoute } from "vue-router";
+export default {
+  name: "OrderDetailPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+    const route = useRoute();
+
+    const frontendBase = ref("");
+    const orderDetail = ref({});
+    const productName = ref("");
+    const packageCategory = ref("");
+    const selectedDate = ref("");
+    const contactInfo = ref({ address: "", email: "", name: "", phone: "" });
+    const passengerInfo = ref([]);
+    const totalPrice = ref(0);
+    const orderStatus = ref("");
+    const skuGroupId = ref("");
+
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in user page: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.goPage("/");
+        });
+    }
+
+    function loadOrderDetail(orderId) {
+      api
+        .get("/api/v1/order/orderDetail/" + orderId)
+        .then((response) => {
+          console.log("order detail: ");
+          console.log(response.data.data);
+          orderDetail.value = response.data.data;
+
+          productName.value = orderDetail.value.meta.productInfo.productName;
+          packageCategory.value =
+            orderDetail.value.meta.productInfo.packageCategory;
+          selectedDate.value = orderDetail.value.meta.productInfo.selectedDate;
+
+          contactInfo.value = orderDetail.value.meta.contactInfo;
+          totalPrice.value = orderDetail.value.price;
+
+          orderStatus.value = orderDetail.value.status;
+
+          passengerInfo.value = orderDetail.value.meta.passengerInfo;
+
+          console.log("passengerInfo.value");
+          console.log(passengerInfo.value);
+
+          skuGroupId.value =
+            orderDetail.value.meta.productInfo.productSkuGroupId;
+        })
+        .catch((e) => {
+          console.log("get order detail err");
+          console.log(e);
+        });
+    }
+
+    function loadFrontendBase() {
+      api
+        .get("/api/public/service")
+        .then(function (response) {
+          console.log("base front url: " + response.data);
+          frontendBase.value = response.data;
+        })
+        .catch(function (error) {
+          console.log("currently not logged in setup: " + error);
+          gp.$goPage("/");
+        });
+    }
+
+    onMounted(() => {
+      whoami();
+      loadOrderDetail(route.params.orderId);
+      loadFrontendBase();
+    });
+
+    return {
+      frontendBase,
+      skuGroupId,
+      productName,
+      packageCategory,
+      totalPrice,
+      orderStatus,
+      selectedDate,
+      passengerInfo,
+      contactInfo,
+      columns: [
+        {
+          name: "surName",
+          required: true,
+          label: "SurName",
+          align: "left",
+          field: "surName",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "givenName",
+          required: true,
+          label: "Given Name",
+          align: "left",
+          field: "givenName",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "nationality",
+          required: true,
+          label: "Nationality",
+          align: "left",
+          field: "nationality",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "birthday",
+          required: true,
+          label: "Birthday",
+          align: "left",
+          field: "birthday",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "gender",
+          required: true,
+          label: "Gender",
+          align: "left",
+          field: "gender",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "passportNo",
+          required: true,
+          label: "Passport No.",
+          align: "left",
+          field: "passportNo",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "passportExpireDate",
+          required: true,
+          label: "Passport Expiry Date",
+          align: "left",
+          field: "passportExpireDate",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+      ],
+    };
+  },
+};
+</script>

+ 257 - 0
hichina-main-front-mobile-first-v2/src/pages/PrivacyPage.vue

@@ -0,0 +1,257 @@
+<template>
+  <q-page>
+    <div style="margin: 0 auto; width: 70">
+      <div class="row">
+        <div class="col-12 text-center text-h4 text-weight-bold">
+          Privacy Policy
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Effective Date: 2023-01-01
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          Welcome to HiChinaTravel. We are committed to protecting the privacy
+          and security of your personal information. This Privacy Policy
+          explains how we collect, use, disclose, and safeguard your information
+          when you use our online travel forum and associated services
+          (collectively, the "Services"). Please read this Privacy Policy
+          carefully to understand our practices regarding your personal
+          information.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Information We Collect
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          1.1 Personal Information
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may collect personal information from you when you voluntarily
+          provide it to us, such as when you create an account, participate in
+          our forum discussions, post comments, or submit inquiries. The
+          personal information we collect may include your name, email address,
+          username, password, and any other information you choose to provide.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          1.2 Non-Personal Information
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may automatically collect certain non-personal information about
+          your use of the Services, such as your IP address, browser type,
+          device type, operating system, referring URLs, and other similar
+          information. This information is used to analyze trends, administer
+          the Services, and gather demographic information about our user base.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          1.3 Cookies and Tracking Technologies
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may use cookies, web beacons, and other tracking technologies to
+          enhance your experience on our website. These technologies allow us to
+          automatically collect information about your browsing activities, such
+          as the pages you visit, the links you click, and other actions you
+          take while using our Services. You can disable cookies through your
+          browser settings, but doing so may limit certain functionalities of
+          the Services.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Use of Information
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          2.1 Provide and Improve Services
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We use the information we collect to provide and improve the Services,
+          customize your experience, respond to your inquiries, and communicate
+          with you about our offerings. We may also use your information to send
+          you promotional materials and other communications, but you can opt
+          out of these communications at any time.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          2.2 Forum Participation
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          When you participate in our forum discussions, any information you
+          choose to disclose, including your username, profile information, and
+          posts, may be publicly visible. Please exercise caution when sharing
+          personal or sensitive information in the forum.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          2.3 Analytics and Aggregated Data
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may use the information we collect to analyze user behavior,
+          monitor trends, measure the effectiveness of our advertising
+          campaigns, and generate aggregated, non-identifiable data for
+          statistical purposes.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h6 text-weight-medium">
+          2.4 Legal Compliance and Protection
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may disclose your information to comply with applicable laws,
+          regulations, legal processes, or governmental requests. We may also
+          disclose your information to enforce our policies, protect our rights,
+          property, or safety, or the rights, property, or safety of others.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Data Retention
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We will retain your personal information for as long as necessary to
+          fulfill the purposes for which it was collected, unless a longer
+          retention period is required or permitted by law. We will securely
+          delete or anonymize your personal information when it is no longer
+          needed.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Data Security
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We have implemented appropriate technical and organizational measures
+          to safeguard the security of your personal information. However,
+          please note that no method of transmission over the internet or method
+          of electronic storage is 100% secure. While we strive to protect your
+          personal information, we cannot guarantee its absolute security.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Third-Party Links and Services
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          Our Services may contain links to third-party websites or services
+          that are not owned or controlled by us. This Privacy Policy does not
+          apply to such third-party websites or services. We encourage you to
+          review the privacy policies of those third parties before providing
+          any personal information or using their services.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Children's Privacy
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          Our Services are not intended for individuals under the age of 13. We
+          do not knowingly collect personal information from children under 13.
+          If you are a parent or guardian and believe that your child has
+          provided personal information to us, please contact us, and we will
+          promptly delete such information from our systems.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          International Data Transfer
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          Your information may be stored and processed in any country where we
+          have facilities or in which we engage service providers. By using the
+          Services, you consent to the transfer of information to countries
+          outside your country of residence, which may have different data
+          protection rules than those of your country.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Your Choices
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          You have the right to access, correct, update, or delete your personal
+          information. You may also have the right to object to or restrict
+          certain processing of your information. To exercise these rights,
+          please contact us using the contact details provided below.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Changes to this Privacy Policy
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          We may update this Privacy Policy from time to time. The updated
+          Privacy Policy will be effective upon posting on our website. Please
+          review this Privacy Policy periodically for any changes. Your
+          continued use of the Services after the posting of the updated Privacy
+          Policy constitutes your acceptance of the changes.
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-h5 text-weight-medium">
+          Contact Us
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          If you have any questions, concerns, or requests regarding this
+          Privacy Policy or our privacy practices, please contact us at
+          <a href="mailto:customerservice@hichinatrip.com"
+            >customerservice@hichinatrip.com</a
+          >
+        </div>
+      </div>
+      <div class="row">
+        <div class="q-pa-md col-12 text-left text-body1 text-weight-regular">
+          Thank you for reading our Privacy Policy. We value your privacy and
+          trust in us.
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+export default {
+  name: "PrivacyPage",
+};
+</script>

+ 872 - 0
hichina-main-front-mobile-first-v2/src/pages/ProductDetailPage.vue

@@ -0,0 +1,872 @@
+<template>
+  <q-page>
+    <div class="row" style="background-color: #e5f2fa; height: 50px">
+      <div class="col-sm-3"></div>
+      <div
+        class="col-12 col-sm-9 text-body1 q-pl-md flex"
+        style="align-items: center"
+      >
+        Travel shop > {{ productTypeName }} > {{ productName }}
+      </div>
+    </div>
+    <div class="row q-pa-md" style="min-height: 340px">
+      <div class="col-12 col-sm-4" style="height: 340px">
+        <q-carousel
+          v-if="renderComponent"
+          v-model="slide"
+          animated
+          navigation
+          infinite
+          height="100%"
+          :autoplay="autoplay"
+          arrows
+          transition-prev="slide-right"
+          transition-next="slide-left"
+          @mouseenter="autoplay = false"
+          @mouseleave="autoplay = 2000"
+        >
+          <q-carousel-slide
+            v-for="(imageUrl, index) in imageContainer.imageList"
+            :key="index"
+            :name="index"
+            :img-src="imageUrl"
+          >
+          </q-carousel-slide>
+        </q-carousel>
+      </div>
+      <div class="col-12 col-sm-8 column" style="height: 340px">
+        <div class="text-h4 q-pl-md" style="min-height: 50px">
+          {{ productName }}
+        </div>
+        <div class="q-ml-md q-pl-md col" style="background-color: #fff9c6">
+          {{ removeHtmlTag(shortProductDescription).substring(0, 500) }}...
+        </div>
+      </div>
+    </div>
+    <div class="row" style="min-height: 500px">
+      <div class="col-12 col-sm-4 row justify-center" style="height: 500px" v-if="isClient & DatePickerloaded" >
+        <!-- https://github.com/shubhadip/vuejs3-datepicker#install -->
+        <!-- <datepicker
+          v-if="renderComponent && productTypeId != LOCALSPECIALTYPRODUCTTYPE"
+          @selected="handleSelectDate"
+          :use-utc="shouldUseUtc"
+          :disabled-dates="state.disabledDates"
+          v-model="selectedDate"
+          :prevent-disable-date-selection="true"
+          :inline="shouldInline"
+          :icon-width="40"
+          calendar-class="calendarclass"
+        ></datepicker> -->
+        <component
+            :is="DatePickerComponent"
+            v-if="renderComponent && productTypeId != LOCALSPECIALTYPRODUCTTYPE"
+          @selected="handleSelectDate"
+          :use-utc="shouldUseUtc"
+          :disabled-dates="state.disabledDates"
+          v-model="selectedDate"
+          :prevent-disable-date-selection="true"
+          :inline="shouldInline"
+          :icon-width="40"
+          calendar-class="calendarclass"
+          />
+      </div>
+      <div
+        class="col-12 col-sm-8 q-pl-xl q-pt-md column"
+        style="min-height: 500px"
+      >
+        <div class="row">
+          <div class="col-3 col-sm-2 text-weight-bold text-h4">Days:</div>
+          <div
+            v-if="productTypeId != FLIGHTPRODUCTTYPE"
+            class="col-11 col-sm-10 flex text-h6 q-pl-sm"
+            style="align-items: center"
+          >
+            {{ days - 1 }} nights {{ days }} days
+          </div>
+          <div
+            v-if="productTypeId === FLIGHTPRODUCTTYPE"
+            class="col-11 col-sm-10 flex text-h6 q-pl-sm"
+            style="align-items: center"
+          >
+            {{ days }} days
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-3 col-sm-2 text-weight-bold text-h4">Option:</div>
+          <div
+            class="col-11 col-sm-10 flex text-h6 q-pl-sm"
+            style="align-items: center"
+          >
+            <q-btn-toggle
+              v-if="productTypeId != LOCALSPECIALTYPRODUCTTYPE"
+              v-model="activeIndex"
+              spread
+              no-caps
+              toggle-color="blue-6"
+              color="white"
+              text-color="black"
+              @click="
+                setActiveCate(packageCategories[activeIndex], activeIndex)
+              "
+              :options="labelValuePair(packageCategories)"
+            />
+            <q-btn-toggle
+              v-if="productTypeId === LOCALSPECIALTYPRODUCTTYPE"
+              v-model="activeIndex"
+              spread
+              no-caps
+              toggle-color="blue-6"
+              color="white"
+              text-color="black"
+              @click="
+                setActiveCateForLocalSpecialty(
+                  packageCategories[activeIndex],
+                  activeIndex
+                )
+              "
+              :options="labelValuePair(packageCategories)"
+            />
+          </div>
+        </div>
+        <div
+          v-if="
+            productTypeId == 'd7b95089-e278-4522-8f71-dacac41ba32f' ||
+            productTypeId == '3a53caed-b788-4290-896d-7922532ad769' ||
+            productTypeId == 'e05d07a3-a717-45b8-b009-47a349890e41'
+          "
+          class="row text-weight-bold text-h4 q-mt-xl"
+        >
+          Passengers
+        </div>
+        <div
+          v-if="productTypeId == HOTELPRODUCTTYPE"
+          class="row text-weight-bold text-h4 q-mt-xl"
+        >
+          Rooms
+        </div>
+        <div class="row q-mt-md" style="border-top: 2px solid black">
+          <div
+            v-if="productTypeId != LOCALSPECIALTYPRODUCTTYPE"
+            class="row col-12 q-pr-md q-mt-md"
+          >
+            <div class="col-11 col-sm-4 q-pr-md">
+              <q-input
+                label="Adult Count"
+                v-model.number="adultCount"
+                type="number"
+                min="1"
+                outlined
+              />
+            </div>
+            <div class="col-11 col-sm-4 q-pr-md">
+              <q-input
+                label="Child Count"
+                v-model.number="childCount"
+                type="number"
+                min="0"
+                outlined
+              />
+            </div>
+            <div class="col-11 col-sm-4 q-pr-md">
+              <q-input
+                label="Infant Count(less than 2 years old)"
+                v-model.number="infantCount"
+                type="number"
+                min="0"
+                outlined
+              />
+            </div>
+          </div>
+          <div
+            v-if="productTypeId === LOCALSPECIALTYPRODUCTTYPE"
+            class="row col-12 q-pr-md q-mt-md"
+          >
+            <div class="col-4 q-pr-md">
+              <q-input
+                label="Count"
+                v-model.number="buyCount"
+                type="number"
+                min="1"
+                outlined
+              />
+            </div>
+          </div>
+          <div class="row col-12" style="height: 50px"></div>
+          <div
+            class="row col-12 q-pr-md q-mt-md"
+            style="background-color: #e9e9e9; min-height: 50px"
+          >
+            <div
+              class="col-4 text-blue-6 text-h5 flex"
+              style="align-items: center; height: 50px"
+            >
+              CNY
+            </div>
+            <div
+              v-if="
+                productTypeId != LOCALSPECIALTYPRODUCTTYPE &&
+                productTypeId != HOTELPRODUCTTYPE
+              "
+              class="col-4 col-sm-3 flex"
+              style="align-items: center; height: 50px"
+            >
+              {{
+                adultCount * adultUnitPrice +
+                childCount * childUnitPrice +
+                infantCount * infantUnitPrice +
+                (adultCount % 2 == 0 ? 0 : 1*singleDiffPrice)
+              }}
+              <label v-if="adultCount % 2 != 0 && singleDiffPrice > 0"
+                >(Include single room difference:{{ singleDiffPrice }})</label
+              >
+            </div>
+            <div
+              v-if="
+                productTypeId == LOCALSPECIALTYPRODUCTTYPE ||
+                productTypeId == HOTELPRODUCTTYPE
+              "
+              class="col-4 col-sm-3 flex"
+              style="align-items: center; height: 50px"
+            >
+              {{ generalPrice * buyCount }}
+            </div>
+            <div
+              class="col-7 col-sm-3 flex"
+              style="align-items: center; height: 50px"
+            >
+              <q-btn
+                class="glossy"
+                rounded
+                color="deep-orange"
+                @click="goPage('/contact')"
+                label="Pre-book Consult"
+              />
+            </div>
+            <div
+              class="col-5 col-sm-2 flex"
+              style="align-items: center; height: 50px"
+            >
+              <q-btn
+                class="glossy"
+                @click="collectConfigAndGoPage()"
+                rounded
+                color="blue-6"
+                label="Book Now"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div
+      class="row q-pa-md"
+      style="min-height: 500px; background-color: #d9d9d9"
+    >
+      <div
+        class="col-12"
+        style="background-color: white"
+        v-html="productDescription"
+      ></div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, onBeforeMount, nextTick, getCurrentInstance } from "vue";
+import { api } from "boot/axios";
+import { useSeoMeta } from "unhead";
+import { useQuasar } from "quasar";
+import { useRoute, useRouter } from "vue-router";
+// import Datepicker from "vuejs3-datepicker";
+import { bookParamStore } from "stores/bookParamStore";
+export default {
+  name: "ProductDetailPage",
+  // components: {
+  //   Datepicker,
+  // },
+  setup() {
+    const store = bookParamStore();
+
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const route = useRoute();
+    const router = useRouter();
+    const buyCount = ref(1);
+    const adultCount = ref(1);
+    const childCount = ref(0);
+    const infantCount = ref(0);
+    const skusInGroup = { skus: [] };
+    const productName = ref("");
+    const productDescription = ref("");
+    const shortProductDescription = ref("");
+    const productTypeId = ref("");
+    const productTypeName = ref("");
+    const packageCategories = ref([]);
+    const imageList = ref([]);
+    const days = ref("");
+    const imageContainer = { imageList: [] };
+    const activeCategory = ref("");
+    const filteredSkuIndex = ref([]);
+    const activeIndex = ref(0);
+    const adultUnitPrice = ref(0);
+    const singleDiffPrice = ref(0);
+    const generalPrice = ref(0);
+    const childUnitPrice = ref(0);
+    const infantUnitPrice = ref(0);
+    const singleMatchedSku = ref({});
+    const selectedDate = ref(new Date());
+    const maxNum = ref(1000000);
+    const renderComponent = ref(true);
+    const totalPrice = ref(0);
+    const isClient = ref(false);
+    const DatePickerComponent = ref({});
+    const DatePickerloaded = ref(false);
+
+    const TYPEOFPACKAGEPROP = "11cd8b32-c4f6-47db-8b8a-486c992bf43b";
+    const LOCALSPECIALTYPRODUCTTYPE = "fd264cab-ee8d-4571-a477-03d7e7c090b3";
+    const HOTELPRODUCTTYPE = "a9f5adbe-c09b-49bc-a614-8a1c5d5e5337";
+    const MAXNUM = "720f4f2e-e114-4003-9806-bc56a9366278";
+    const PRODUCTIMAGEURLPROP = "2dea54f4-9b9c-413a-8b3a-0caf273283d2";
+    const DAYSPROP = "8cc865ff-b30f-4f00-b426-9e64418e5100";
+    const AVAILABLEDATEPROP = "f0b807e5-d1a6-4454-a400-7905a4fea492";
+    const TOURPRODUCTTYPE = "3a53caed-b788-4290-896d-7922532ad769";
+    const FLIGHTHOTELPRODUCTTYPE = "e05d07a3-a717-45b8-b009-47a349890e41";
+    const FLIGHTPRODUCTTYPE = "d7b95089-e278-4522-8f71-dacac41ba32f";
+    const GENERALPRICE = "e16df480-b17d-4442-91c2-d6c30d0d7ab0";
+    const ADULTPRICEPROP = "e228b843-e054-41f8-91dd-19663460df54";
+    const CHILDPRICEPROP = "c4c845a7-4bef-46d8-a5ad-d72a5464e8b1";
+    const SINGLEDIFFPRICEPROP = "d4825026-3ad8-4861-8e8f-e980b55fe67b";
+    const INFANTPRICEPROP = "448406cb-b68f-439e-9da8-148d78ae8404";
+    const SHORTDESCRIPTIONPROP = "f3cac6ed-8e87-4fb9-a912-bd87f483b4d5";
+
+    const forceRerender = async () => {
+      // Remove MyComponent from the DOM
+      renderComponent.value = false;
+
+      // Wait for the change to get flushed to the DOM
+      await nextTick();
+
+      // Add the component back in
+      renderComponent.value = true;
+    };
+
+    function getTotalPrice() {
+      if (
+        productTypeId.value != LOCALSPECIALTYPRODUCTTYPE &&
+        productTypeId.value != HOTELPRODUCTTYPE
+      ) {
+        totalPrice.value =
+          adultCount.value * adultUnitPrice.value +
+          childCount.value * childUnitPrice.value +
+          infantCount.value * infantUnitPrice.value +
+          (adultCount.value % 2 == 0 ? 0 : 1*singleDiffPrice.value);
+      } else {
+        totalPrice.value = generalPrice.value * buyCount.value;
+      }
+      console.log("this is the total price:");
+      console.log(totalPrice.value);
+      return totalPrice.value;
+    }
+
+    function collectConfigAndGoPage() {
+      if (singleMatchedSku.value == null) {
+        gp.$generalNotify($q, false, "Not all required config are selected");
+        return;
+      }
+      var params = {};
+      params.productTypeId = productTypeId.value;
+      params.productName = productName.value;
+      params.productSkuId =
+        singleMatchedSku.value["hichinaProductBasicDTO"]["skuId"];
+      params.productSkuGroupId = route.params.skuGroupId;
+      params.packageCategory = activeCategory.value;
+      params.productTypeName = productTypeName.value;
+      params.profileImageUrl = extractAttributeValueFromProductPropertyBag(
+        singleMatchedSku.value,
+        PRODUCTIMAGEURLPROP
+      );
+      params.totalPrice = getTotalPrice();
+      //params.totalPrice = 1;
+      if (productTypeId.value != LOCALSPECIALTYPRODUCTTYPE) {
+        params.adultCount = adultCount.value;
+        params.childCount = childCount.value;
+        params.infantCount = infantCount.value;
+      }
+      if (LOCALSPECIALTYPRODUCTTYPE != productTypeId.value) {
+        params.selectedDate = selectedDate.value.toISOString().split("T")[0];
+      }
+      if (
+        productTypeId.value == LOCALSPECIALTYPRODUCTTYPE ||
+        productTypeId.value == HOTELPRODUCTTYPE
+      ) {
+        params.buyCount = buyCount.value;
+      }
+
+      console.log("params collected from product");
+      console.log(params);
+      // gp.$goPageWithParams(val, params);
+      // router.push({ name: val, state: params });
+      gp.$goPage("/book");
+      store.setOrderDetail(params);
+    }
+
+    function handleSelectDate(dt) {
+      console.log("dt");
+      console.log(dt);
+      console.log("filteredSkuIndex.value");
+      console.log(filteredSkuIndex.value);
+
+      console.log("skusInGroup.skus");
+      console.log(skusInGroup.skus);
+
+      var matchedIndex = -1;
+      for (var i in filteredSkuIndex.value) {
+        var index = filteredSkuIndex.value[i];
+        var availableDatesInString =
+          extractAttributeValueFromProductPropertyBag(
+            skusInGroup.skus[index],
+            AVAILABLEDATEPROP
+          );
+        console.log("availableDatesInString");
+        console.log(availableDatesInString);
+
+        var dateObjArray = multiDateString2DateObjectArray(
+          availableDatesInString
+        );
+        console.log("dateObjArray");
+        console.log(dateObjArray);
+        if (dateObjArray.some((e) => e.toDateString() == dt.toDateString())) {
+          matchedIndex = index;
+          break;
+        }
+      }
+      console.log("matchedIndex");
+      console.log(matchedIndex);
+      singleMatchedSku.value = skusInGroup.skus[matchedIndex];
+
+      maxNum.value = extractAttributeValueFromProductPropertyBag(
+        singleMatchedSku.value,
+        MAXNUM
+      );
+
+      console.log("maxNum");
+      console.log(maxNum.value);
+      setPrice(singleMatchedSku.value);
+    }
+
+    var state = {
+      disabledDates: {
+        to: new Date(2023, 3, 15), // Disable all dates up to specific date
+        from: new Date(2023, 4, 20), // Disable all dates after specific date
+
+        dates: [
+          // Disable an array of dates
+          new Date(2016, 9, 16),
+          new Date(2016, 9, 17),
+          new Date(2016, 9, 18),
+        ],
+        preventDisableDateSelection: true,
+      },
+    };
+
+    function logPv() {
+      api
+        .post(
+          "/api/public/pagestats/pv/product-detail-" + route.params.skuGroupId
+        )
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    function logView() {
+      api
+        .post("/api/public/pagestats/view-product/" + route.params.skuGroupId)
+        .then((res) => {
+          console.log("view cnt of this product:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    function extractAttributeValueFromProductPropertyBag(inputObject, attrId) {
+      var valArray = inputObject.productPropertyBag.filter((obj) => {
+        return obj.attributeId == attrId;
+      });
+      if (valArray.length > 0) {
+        return valArray[0].attributeValue;
+      }
+      return null;
+    }
+
+    function getFilteredSkuIndexAndSetActiveValue(item, index) {
+      activeCategory.value = item;
+      filteredSkuIndex.value = [];
+      for (var i in skusInGroup.skus) {
+        var sku = skusInGroup.skus[i];
+        var selectedSkuPropertyBags = sku["productPropertyBag"].filter(
+          (obj) => {
+            return obj.attributeId == TYPEOFPACKAGEPROP;
+          }
+        );
+        if (selectedSkuPropertyBags[0].attributeValue == item) {
+          filteredSkuIndex.value.push(i);
+        }
+      }
+      activeIndex.value = index;
+    }
+
+    function multiDateString2DateObjectArray(candidateAvailableDates) {
+      var dateObjArray = [];
+      var datestringArray = candidateAvailableDates.split(";");
+      for (var i in datestringArray) {
+        var datestr = datestringArray[i];
+        if (datestr != null && datestr.length > 0) {
+          var dateObj = new Date(datestr);
+          // console.log("...converting string to date:"+ datestr)
+          // console.log(dateObj)
+          dateObjArray.push(dateObj);
+        }
+      }
+      return dateObjArray;
+    }
+
+    function compareDate(a, b) {
+      if (a.getDate() < b.getDate()) {
+        return -1;
+      }
+      if (a.getDate() > b.getDate()) {
+        return 1;
+      }
+      return 0;
+    }
+
+    function setCandidateAvailableDates(indexArray) {
+      var candidateAvailableDates = "";
+      for (var i in indexArray) {
+        var availableDates = extractAttributeValueFromProductPropertyBag(
+          skusInGroup.skus[indexArray[i]],
+          AVAILABLEDATEPROP
+        );
+        candidateAvailableDates = candidateAvailableDates + availableDates;
+      }
+
+      var dateObjArray = multiDateString2DateObjectArray(
+        candidateAvailableDates
+      );
+
+      dateObjArray = dateObjArray.sort((a, b) => a - b);
+      console.log("dateObjArray");
+      console.log(dateObjArray);
+
+      var minDate = dateObjArray[0];
+      var maxDate = dateObjArray.slice(-1)[0];
+
+      state.disabledDates.to = minDate;
+      var nextDayOfMax = new Date(maxDate);
+      nextDayOfMax.setDate(nextDayOfMax.getDate() + 1);
+      state.disabledDates.from = nextDayOfMax;
+
+      var segmentDisabledDates = [];
+      var pointer = new Date(minDate);
+
+      while (pointer < maxDate) {
+        // check contains
+        if (
+          !dateObjArray.some((e) => e.toDateString() == pointer.toDateString())
+        ) {
+          segmentDisabledDates.push(new Date(pointer));
+        } else {
+        }
+        // update pointer
+        pointer.setDate(pointer.getDate() + 1);
+      }
+      state.disabledDates.dates = segmentDisabledDates;
+
+      forceRerender();
+    }
+
+    function setPrice(sku) {
+      if (sku == null) {
+        if (
+          productTypeId.value == LOCALSPECIALTYPRODUCTTYPE ||
+          productTypeId.value == HOTELPRODUCTTYPE
+        ) {
+          console.log("001");
+          generalPrice.value = 0;
+        } else {
+          adultUnitPrice.value = 0;
+          childUnitPrice.value = 0;
+          infantUnitPrice.value = 0;
+        }
+      } else {
+        if (
+          productTypeId.value == LOCALSPECIALTYPRODUCTTYPE ||
+          productTypeId.value == HOTELPRODUCTTYPE
+        ) {
+          console.log("003");
+          generalPrice.value = extractAttributeValueFromProductPropertyBag(
+            sku,
+            GENERALPRICE
+          );
+        } else {
+          console.log("004");
+          adultUnitPrice.value = extractAttributeValueFromProductPropertyBag(
+            sku,
+            ADULTPRICEPROP
+          );
+          singleDiffPrice.value = extractAttributeValueFromProductPropertyBag(
+            sku,
+            SINGLEDIFFPRICEPROP
+          );
+          childUnitPrice.value = extractAttributeValueFromProductPropertyBag(
+            sku,
+            CHILDPRICEPROP
+          );
+          infantUnitPrice.value = extractAttributeValueFromProductPropertyBag(
+            sku,
+            INFANTPRICEPROP
+          );
+        }
+      }
+    }
+
+    function setActiveDescription() {
+      var indexToFilter =
+        filteredSkuIndex.value.length < 1 ? 0 : filteredSkuIndex.value[0];
+      productDescription.value =
+        skusInGroup.skus[indexToFilter]["hichinaProductBasicDTO"][
+          "productContent"
+        ];
+
+      shortProductDescription.value =
+        extractAttributeValueFromProductPropertyBag(
+          skusInGroup.skus[indexToFilter],
+          SHORTDESCRIPTIONPROP
+        );
+    }
+
+    function setActiveCate(item, index) {
+      console.log("picking package cate: " + item);
+      console.log("index of it is:   " + index);
+      getFilteredSkuIndexAndSetActiveValue(item, index);
+      setCandidateAvailableDates(filteredSkuIndex.value);
+      setPrice(null);
+      selectedDate.value = new Date();
+      singleMatchedSku.value = null;
+      setActiveDescription();
+    }
+
+    function setTourProductData(inputArray) {
+      packageCategories.value = [];
+      imageList.value = [];
+      for (var index in inputArray) {
+        var packageCat = extractAttributeValueFromProductPropertyBag(
+          inputArray[index],
+          TYPEOFPACKAGEPROP
+        );
+        if (packageCat != null) {
+          packageCategories.value.push(packageCat);
+        }
+
+        var url = extractAttributeValueFromProductPropertyBag(
+          inputArray[index],
+          PRODUCTIMAGEURLPROP
+        );
+        if (url != null) {
+          // imageContainer.imageList.push(url);
+          imageList.value.push(url);
+        }
+
+        // this if means only need to calculate days once because all this property in the same sku group should be same
+        if (days.value == "") {
+          days.value = extractAttributeValueFromProductPropertyBag(
+            inputArray[index],
+            DAYSPROP
+          );
+        }
+      }
+      imageContainer.imageList = imageList.value;
+      forceRerender();
+      // trick: remove duplicates
+      packageCategories.value = [...new Set(packageCategories.value)];
+      // force select the first package category on entering page
+      setActiveCate(packageCategories.value[0], 0);
+    }
+
+    function labelValuePair(valArray) {
+      var ret = [];
+      valArray.forEach((x, i) => {
+        var obj = { label: x, value: i };
+        ret.push(obj);
+      });
+      return ret;
+    }
+
+    function setFlightHotelProductData(inputArray) {
+      setTourProductData(inputArray);
+    }
+
+    function setHotelProductData(inputArray) {
+      setTourProductData(inputArray);
+    }
+
+    function setFligtProductData(inputArray) {
+      setTourProductData(inputArray);
+    }
+
+    function setLocalSpecialtyProductData(inputArray) {
+      packageCategories.value = [];
+      imageList.value = [];
+      for (var index in inputArray) {
+        var packageCat = extractAttributeValueFromProductPropertyBag(
+          inputArray[index],
+          TYPEOFPACKAGEPROP
+        );
+        if (packageCat != null) {
+          packageCategories.value.push(packageCat);
+        }
+
+        var url = extractAttributeValueFromProductPropertyBag(
+          inputArray[index],
+          PRODUCTIMAGEURLPROP
+        );
+        if (url != null) {
+          imageList.value.push(url);
+        }
+      }
+      imageContainer.imageList = imageList.value;
+      // trick: remove duplicates
+      packageCategories.value = [...new Set(packageCategories.value)];
+      // force select the first package category on entering page
+      setActiveCateForLocalSpecialty(packageCategories.value[0], 0);
+    }
+
+    function processSkuGroups(inputArray) {
+      if (inputArray.length > 0) {
+        productName.value =
+          inputArray[0]["hichinaProductBasicDTO"]["productName"];
+        productTypeId.value =
+          inputArray[0]["hichinaProductBasicDTO"]["productTypeId"];
+        productTypeName.value =
+          inputArray[0]["hichinaProductBasicDTO"]["productTypeName"];
+
+        useSeoMeta({
+          title: productName.value,
+          description: productName.value,
+          ogDescription: productName.value,
+          ogTitle: productName.value,
+          ogImage: "https://www.hichinatravel.com/static/png/name-67280b81.png",
+          twitterCard: "summary_large_image",
+        });
+
+        if (productTypeId.value === TOURPRODUCTTYPE) {
+          // 跟团游,则按照跟团游的页面模板来设置各个参数
+          setTourProductData(inputArray);
+        } else if (productTypeId.value === FLIGHTHOTELPRODUCTTYPE) {
+          // 机票酒店套餐
+          setFlightHotelProductData(inputArray);
+        } else if (productTypeId.value === FLIGHTPRODUCTTYPE) {
+          // 机票
+          setFligtProductData(inputArray);
+        } else if (productTypeId.value === HOTELPRODUCTTYPE) {
+          // 酒店
+          setHotelProductData(inputArray);
+        } else if (productTypeId.value === LOCALSPECIALTYPRODUCTTYPE) {
+          // 本地土特产
+          setLocalSpecialtyProductData(inputArray);
+        }
+      }
+    }
+
+    function loadSkusInGroup() {
+      var params = {};
+      params.skuGroupId = route.params.skuGroupId;
+      api
+        .get("/api/public/productsku/bygroupidwithpropertybag", {
+          params: params,
+        })
+        .then(function (response) {
+          skusInGroup.skus = response.data.data;
+          console.log("skusInGroup.skus in loadSkusInGroup");
+          console.log(skusInGroup.skus);
+          processSkuGroups(skusInGroup.skus);
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    onBeforeMount(() => {
+      isClient.value = process.env.CLIENT;
+
+      if (process.env.CLIENT) {
+
+        import("vuejs3-datepicker")
+          .then((module) => {
+            // Use the module here
+            console.log("imported vuejs3-datepicker");
+            console.log(module.default);
+            DatePickerComponent.value = module.default;
+            DatePickerloaded.value = true;
+          })
+          .catch((error) => {
+            console.error("Error importing module ckeditor5-vue:", error);
+          });
+      }
+    });
+
+    onMounted(() => {
+      logPv();
+      logView();
+      loadSkusInGroup();
+    });
+    return {
+      DatePickerComponent,
+      DatePickerloaded,
+      isClient,
+      productTypeName,
+      productName,
+      productTypeId,
+      days,
+      slide: ref(0),
+      autoplay: ref(true),
+      imageContainer,
+      productDescription,
+      shortProductDescription,
+      handleSelectDate,
+      selectedDate,
+      state,
+      renderComponent,
+      LOCALSPECIALTYPRODUCTTYPE,
+      FLIGHTPRODUCTTYPE,
+      HOTELPRODUCTTYPE,
+      packageCategories,
+      labelValuePair,
+      setActiveCate,
+      activeIndex,
+      adultCount,
+      childCount,
+      infantCount,
+      adultUnitPrice,
+      singleDiffPrice,
+      childUnitPrice,
+      infantUnitPrice,
+      generalPrice,
+      buyCount,
+      shouldUseUtc: ref(true),
+      shouldInline: ref(true),
+      collectConfigAndGoPage,
+    };
+  },
+};
+</script>

+ 33 - 0
hichina-main-front-mobile-first-v2/src/pages/RegSuccessPage.vue

@@ -0,0 +1,33 @@
+<template>
+  <q-page>
+    <div class="row flex flex-center">
+      <div class="col-12 col-md-8 text-h3 q-pa-xl text-red">
+        Please go to your mailbox to confirm registration...
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { onMounted, getCurrentInstance } from "vue";
+import { useQuasar } from "quasar";
+export default {
+  name: "RegSuccessPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const waitAndGo = async () => {
+      setTimeout(() => {
+        gp.$goPage("/");
+      }, 8000);
+    };
+    onMounted(() => {
+      waitAndGo();
+    });
+    return {};
+  },
+};
+</script>

+ 126 - 0
hichina-main-front-mobile-first-v2/src/pages/RegisterPage.vue

@@ -0,0 +1,126 @@
+<template>
+  <q-page>
+    <div style="height: 82px"></div>
+    <div class="row q-mb-xl justify-center">
+      <div
+        class="col-10 col-sm-5 col-md-3 rounded-borders login-border shadow-7"
+      >
+        <div class="text-h5 text-left text-weight-bold text-black q-pa-md">
+          {{ $t("register") }}
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-input
+            :rules="[(val) => !!val || 'Field is required']"
+            color="blue-12"
+            v-model="username"
+            :label="$t('enter_email')"
+            ref="usernameInput"
+          >
+            <template v-slot:prepend>
+              <q-icon name="account_circle" />
+            </template>
+          </q-input>
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-input
+            :rules="[(val) => !!val || 'Field is required']"
+            color="blue-12"
+            v-model="password"
+            type="password"
+            :label="$t('enter_password')"
+            ref="passwordInput"
+          >
+            <template v-slot:prepend>
+              <q-icon name="lock" />
+            </template>
+          </q-input>
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-input
+            color="blue-12"
+            v-model="confPassword"
+            error-message="Confirm password is not the same with password"
+            :error="!correctConfPass"
+            type="password"
+            :label="$t('confirm_password')"
+            ref="confPasswordInput"
+          >
+            <template v-slot:prepend>
+              <q-icon name="lock" />
+            </template>
+          </q-input>
+        </div>
+        <div class="col-12 q-pa-md">
+          <q-btn color="primary" @click="register()" :label="$t('register')" />
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance, computed } from "vue";
+import { useQuasar } from "quasar";
+import { api } from "boot/axios";
+
+export default {
+  name: "RegisterPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const username = ref("");
+    const password = ref("");
+    const confPassword = ref("");
+
+    function register() {
+      const isValidPassword = instance.refs.passwordInput.validate();
+      const isValidConfPassword = instance.refs.confPasswordInput.validate();
+      const isValidUsername = instance.refs.usernameInput.validate();
+
+      if (
+        isValidPassword &&
+        isValidConfPassword &&
+        isValidUsername &&
+        confPassword.value == password.value
+      ) {
+        // do real reg logic
+        var data = {
+          email: username.value,
+          password: password.value,
+        };
+
+        gp.$showLoading($q);
+        api
+          .post("/api/public/register", data)
+          .then((response) => {
+            console.log("==after register");
+            console.log(response.data);
+            if (response.data.ok == true) {
+              gp.$generalNotify($q, true, "Succeed sending registering info");
+            } else {
+              gp.$generalNotify($q, false, response.data.message);
+            }
+            gp.$hideLoading($q);
+            gp.$goPage("/regsuccess");
+          })
+          .catch((e) => {
+            gp.$hideLoading($q);
+            gp.$generalNotify($q, false, "Error message: " + e);
+          });
+      } else {
+        gp.$generalNotify($q, false, "Input not valid");
+      }
+    }
+    return {
+      password,
+      confPassword,
+      username,
+      register,
+      correctConfPass: computed(() => confPassword.value == password.value),
+    };
+  },
+};
+</script>

+ 64 - 0
hichina-main-front-mobile-first-v2/src/pages/RegvalidationPage.vue

@@ -0,0 +1,64 @@
+<template>
+  <q-page>
+    <div class="row flex flex-center">
+      <div v-if="!validated" class="text-h5 text-red">Validating...</div>
+      <div v-if="validated" class="text-h5 text-red">
+        Your register has finished, please go to login page
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance } from "vue";
+import { api } from "boot/axios";
+import { useRoute } from "vue-router";
+import { useQuasar } from "quasar";
+export default {
+  name: "RegvalidationPage",
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const validated = ref(false);
+
+    const route = useRoute();
+
+    const waitAndGo = async () => {
+      setTimeout(() => {
+        gp.$goPage("/auth/login");
+      }, 2500);
+    };
+
+    onMounted(() => {
+      console.log("on mounted");
+      api
+        .post("/api/public/register/pushintodb/" + route.params.regKey)
+        .then((response) => {
+          console.log("Validating register result: ");
+          console.log(response.data);
+          if (response.data.ok == true) {
+            validated.value = true;
+            waitAndGo();
+          } else {
+            gp.$generalNotify(
+              $q,
+              false,
+              "Validating registering failed: " + response.data.message
+            );
+          }
+        })
+        .catch((e) => {
+          console.log("Validate registering fail detail err");
+          gp.$generalNotify($q, false, "Validating registering failed: " + e);
+          console.log(e);
+        });
+    });
+    return {
+      validated,
+    };
+  },
+};
+</script>

+ 225 - 0
hichina-main-front-mobile-first-v2/src/pages/TravelShopPage.vue

@@ -0,0 +1,225 @@
+<template>
+  <q-page>
+    <div
+      class="row justify-center"
+      style="height: 376px; background-color: #2a82e4; position: relative"
+    >
+      <div
+        class="col-12 col-sm-9 col-md-7 flex flex-center row shadow-7"
+        style="
+          background-color: white;
+          height: 180px;
+          position: absolute;
+          top: 120px;
+          border-radius: 10px;
+        "
+      >
+        <q-input
+          class="col-10"
+          rounded
+          outlined
+          v-model="query"
+          @update:model-value="(val) => updateQuery(val)"
+          :label="$t('search_by_title')"
+        />
+      </div>
+      <q-btn-toggle
+        class="col-11 col-sm-8 col-md-6 row shadow-7"
+        style="
+          background-color: white;
+          height: 95px;
+          position: absolute;
+          top: 70px;
+          border-radius: 20px;
+        "
+        @click="setActiveTab(btnToggle)"
+        v-model="btnToggle"
+        toggle-color="primary"
+        :options="[
+          { label: $t('group_tour'), value: 'groupTour', icon: 'group' },
+          { label: $t('hotel_deals'), value: 'hotelDeals', icon: 'business' },
+          { label: $t('flight_deals'), value: 'flightDeals', icon: 'flight' },
+          {
+            label: $t('holiday_package'),
+            value: 'holidayPackage',
+            icon: 'beach_access',
+          },
+          {
+            label: $t('china_stuff'),
+            value: 'chinaStuff',
+            icon: 'shopping_cart',
+          },
+        ]"
+      />
+    </div>
+    <div class="row" style="height: 50px"></div>
+    <div class="row justify-center text-blue text-h4 q-mt-xl">
+      {{ $t("deals_for_you") }}
+    </div>
+    <div class="row justify-left">
+      <div
+        class="col-12 col-sm-6 col-md-3"
+        v-for="(item, index) in globalProductList"
+        v-bind:key="index"
+      >
+        <div class="q-pa-md">
+          <q-card class="cursor-pointer" flat bordered style="height: 500px">
+            <q-img
+              @click="goPage('/product-detail/' + item.skuGroupId)"
+              :src="item.imageUrl"
+              style="height: 300px"
+              placeholder-src="https://photoprism.hichinatravel.com/api/v1/t/2bfc32550ae040956f7e861566d26c487c0143e7/32mcf2k4/tile_224"
+            />
+
+            <q-card-section style="max-height: 200px; overflow: hidden">
+              <div class="text-subtitle1 text-pink">
+                Start from ¥{{ item.minPrice }}
+              </div>
+              <div class="text-h5 q-mt-sm q-mb-xs">
+                <a :href="'./product-detail/' + item.skuGroupId">{{
+                  item.skuGroupName
+                }}</a>
+              </div>
+            </q-card-section>
+          </q-card>
+        </div>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-12">
+        <p class="text-center" style="background-color: #b4b4b4">
+          Scroll to load more
+        </p>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { debounce } from "lodash";
+import { api } from "boot/axios";
+export default {
+  name: "TravelShopPage",
+  setup() {
+    const btnToggle = ref("groupTour");
+    const query = ref("");
+    const globalProductList = ref([]);
+    const currentPage = ref(1);
+    const pageSize = ref(100);
+    const productTypeId = ref("");
+    const productList = ref([]);
+
+    const totalProductCount = ref(-1);
+
+    const TOURPACKAGETYPE = "3a53caed-b788-4290-896d-7922532ad769";
+    const HOTELPACKAGETYPE = "a9f5adbe-c09b-49bc-a614-8a1c5d5e5337";
+    const FLIGTHPACKAGETYPE = "d7b95089-e278-4522-8f71-dacac41ba32f";
+    const HOLIDAYPACKAGETYPE = "e05d07a3-a717-45b8-b009-47a349890e41";
+    const CHINALOCALPACKAGETYPE = "fd264cab-ee8d-4571-a477-03d7e7c090b3";
+
+    const updateQuery = debounce((value) => {
+      console.log(value);
+      globalProductList.value = [];
+      currentPage.value = 1;
+      loadAllProducts();
+    }, 500);
+
+    function loadAllProducts() {
+      var params = {};
+      params.pageSize = pageSize.value;
+      params.page = currentPage.value;
+      params.query = query.value;
+      params.productTypeId = productTypeId.value;
+      api
+        .get("/api/public/productsku/productskugrouplist", { params: params })
+        .then(function (response) {
+          console.log("response.data.data");
+          console.log(response.data.data);
+          productList.value = response.data.data.data;
+          if (totalProductCount.value == -1) {
+            totalProductCount.value = response.data.data.total;
+          }
+          globalProductList.value = globalProductList.value.concat(
+            productList.value
+          );
+          console.log("globalProductList....");
+          console.log(globalProductList.value);
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+
+    function setActiveTab(index) {
+      globalProductList.value = [];
+      currentPage.value = 1;
+      console.log("activating..." + index);
+      btnToggle.value = index;
+      if (btnToggle.value == "groupTour") {
+        productTypeId.value = TOURPACKAGETYPE;
+      } else if (btnToggle.value == "hotelDeals") {
+        productTypeId.value = HOTELPACKAGETYPE;
+      } else if (btnToggle.value == "flightDeals") {
+        productTypeId.value = FLIGTHPACKAGETYPE;
+      } else if (btnToggle.value == "holidayPackage") {
+        productTypeId.value = HOLIDAYPACKAGETYPE;
+      } else if (btnToggle.value == "chinaStuff") {
+        productTypeId.value = CHINALOCALPACKAGETYPE;
+      } else {
+        productTypeId.value = "";
+      }
+      loadAllProducts();
+    }
+
+    function loadMore() {
+      currentPage.value += 1;
+      var maxPage = totalProductCount.value / pageSize.value;
+      if (currentPage.value <= maxPage + 1) {
+        loadAllProducts();
+      } else {
+        console.log("Notify no more pages");
+      }
+    }
+
+    function getNextBatch() {
+      window.onscroll = debounce(function () {
+        let bottomOfWindow =
+          document.documentElement.scrollTop + window.innerHeight + 100 >
+          document.documentElement.scrollHeight;
+
+        if (bottomOfWindow) {
+          loadMore();
+        }
+      }, 500);
+    }
+
+    function logPv() {
+      api
+        .post("/api/public/pagestats/pv/product")
+        .then((res) => {
+          console.log("log pv:");
+          console.log(res.data);
+        })
+        .catch((err) => {
+          console.error("Error:", err);
+        });
+    }
+
+    onMounted(() => {
+      logPv();
+      setActiveTab("groupTour");
+      getNextBatch();
+    });
+
+    return {
+      query,
+      btnToggle,
+      globalProductList,
+      updateQuery,
+      setActiveTab,
+    };
+  },
+};
+</script>
+<style lang="sass" scoped></style>

+ 296 - 0
hichina-main-front-mobile-first-v2/src/pages/UserInfoPage.vue

@@ -0,0 +1,296 @@
+<template>
+  <q-page>
+    <div class="row">
+      <div class="col-md-2"></div>
+      <div class="q-px-md col-12 col-md-8">
+        <div class="q-pa-md">
+          <my-upload
+            field="imageFile"
+            :width="300"
+            :height="300"
+            :url="serviceBase + '/api/v1/image/upload'"
+            :params="upload_icon_params"
+            @crop-upload-success="cropUploadSuccess"
+            lang-type="en"
+            :withCredentials="true"
+            v-model="showImageUpload"
+            img-format="png"
+          ></my-upload>
+        </div>
+        <div class="row">
+          <div style="width: 200px">
+            <q-img :src="updatePackage.profileImageUrl" />
+          </div>
+          <div class="col q-px-md">
+            <div class="text-weight-bold text-h4">My Profile Image</div>
+            <q-btn
+              @click="showUpload"
+              round
+              color="secondary"
+              icon="cloud_upload"
+            />
+            <p class="q-py-md notice mt-16">
+              Support format of jpg.png.bmp, and Picture size is limited at 5M
+            </p>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input
+              outlined
+              v-model="updatePackage.username"
+              label="Username"
+            />
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input outlined v-model="updatePackage.email" label="Email" />
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input outlined v-model="updatePackage.phone" label="Phone" />
+          </div>
+        </div>
+        <div class="row q-py-md">
+          <div class="col-12 col-sm-6 col-md-5">
+            <div class="text-weight-bold">Nationality:</div>
+            <country-select
+              v-model="updatePackage.nationality"
+              :country="updatePackage.nationality"
+              topCountry="US"
+            />
+          </div>
+        </div>
+        <div class="row">
+          <q-btn
+            class="glossy"
+            @click="updateInfo"
+            color="primary"
+            label="Update"
+          />
+        </div>
+        <div
+          class="row"
+          style="min-height: 10px; border-bottom: 1px solid gray"
+        ></div>
+        <div class="row q-py-md">
+          <div class="text-weight-bold text-h5">Change my password</div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input
+              outlined
+              type="password"
+              v-model="currentPass"
+              label="Current Pasword"
+            />
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input
+              outlined
+              type="password"
+              v-model="newPass"
+              label="New Pasword"
+            />
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-12 col-sm-6 col-md-5">
+            <q-input
+              outlined
+              type="password"
+              v-model="confirmPass"
+              label="Confirm Pasword"
+            />
+          </div>
+        </div>
+        <div class="row q-py-md">
+          <q-btn
+            class="glossy"
+            color="primary"
+            @click="updatePassword"
+            label="Update Password"
+          />
+        </div>
+      </div>
+      <div class="col-md-2"></div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { ref, onMounted, getCurrentInstance } from "vue";
+import { useQuasar } from "quasar";
+import myUpload from "vue-image-crop-upload";
+import { api } from "boot/axios";
+export default {
+  name: "UserInfoPage",
+  components: {
+    "my-upload": myUpload,
+  },
+  setup() {
+    const instance = getCurrentInstance();
+    const app = getCurrentInstance().appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const serviceBase = ref("");
+    const country = ref("");
+    const updatePackage = ref({});
+    const upload_icon_params = ref({ expectedType: "thumbnail" });
+    const showImageUpload = ref(false);
+    const currentPass = ref("");
+    const newPass = ref("");
+    const confirmPass = ref("");
+
+    function showUpload() {
+      showImageUpload.value = true;
+    }
+
+    function updateInfo() {
+      console.log(updatePackage.value);
+      api
+        .put("/api/v1/user/basicInfo", updatePackage.value)
+        .then((res) => {
+          console.log(res.data);
+          if (res.data.ok == true) {
+            gp.$generalNotify($q, true, "Succeed udpating basic info");
+          } else {
+            gp.$generalNotify($q, false, "Failed udpating basic info");
+          }
+        })
+        .catch((err) => {
+          gp.$generalNotify($q, false, "Fail creating order" + err);
+        });
+    }
+
+    function updatePassword() {
+      if (
+        currentPass.value == null ||
+        currentPass.value.length < 1 ||
+        newPass.value == null ||
+        newPass.value.length < 1
+      ) {
+        gp.$generalNotify($q, false, "Cannot left any password empty");
+        return;
+      }
+      if (newPass.value != confirmPass.value) {
+        gp.$generalNotify(
+          $q,
+          false,
+          "Confirm password not the same with new Password"
+        );
+        return;
+      }
+      var params = {};
+      params.oldPass = currentPass.value;
+      params.newPass = newPass.value;
+      api
+        .put("/api/v1/user/updatePass", params)
+        .then((res) => {
+          if (res.data.ok == true) {
+            gp.$generalNotify($q, true, "Succeed updating password");
+          } else {
+            gp.$generalNotify($q, false, res.data.message);
+          }
+        })
+        .catch((err) => {
+          gp.$generalNotify($q, false, "Fail updating password " + err);
+        });
+    }
+
+    function getServiceBase() {
+      api
+        .get("/api/public/service/backend-service")
+        .then(function (response) {
+          console.log("service base:");
+          console.log(response.data);
+          serviceBase.value = response.data;
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+    function updateMyProfileImage(url) {
+      var params = {};
+      params.param = url;
+      api
+        .post("/api/v1/user/update-profile-image", params)
+        .then((res) => {
+          gp.$generalNotify($q, true, "Succeed updating profile image");
+          updatePackage.value.profileImageUrl = url;
+        })
+        .catch((err) => {
+          gp.$generalNotify(
+            $q,
+            false,
+            "Fail updating user profile image" + err
+          );
+        });
+    }
+    function cropUploadSuccess(jsonData, field) {
+      var url = jsonData.data;
+      updateMyProfileImage(url);
+      gp.$generalNotify($q, true, "上传成功");
+      showImageUpload.value = false;
+    }
+    function whoami() {
+      api
+        .get("/api/v1/user/whoami")
+        .then(function (response) {
+          console.log("current user in user-info: " + response.data);
+        })
+        .catch(function (error) {
+          console.log("currently not logged in user-info: " + error);
+          gp.$goPage("/auth/login");
+        });
+    }
+    function getMyPrincipal() {
+      api
+        .get("/api/v1/user/mysecurityinfo")
+        .then(function (response) {
+          console.log("my security data:");
+          console.log(response.data.data);
+          if (response.data.ok == true) {
+            country.value = updatePackage.value.nationality;
+            updatePackage.value = response.data.data;
+            if (
+              updatePackage.value.profileImageUrl == null ||
+              updatePackage.value.profileImageUrl.length < 1
+            ) {
+              updatePackage.value.profileImageUrl =
+                "https://photoprism.hichinatravel.com/api/v1/t/07dac61cc5dfec34dd9358f37bd70ce32de68488/32mcf2k4/fit_2048";
+            }
+          } else {
+            gp.$generalNotify($q, false, response.data.message);
+          }
+        })
+        .catch(function (error) {
+          console.log(error);
+        });
+    }
+    onMounted(() => {
+      whoami();
+      getServiceBase();
+      getMyPrincipal();
+    });
+    return {
+      showImageUpload,
+      cropUploadSuccess,
+      upload_icon_params,
+      serviceBase,
+      showUpload,
+      updatePassword,
+      updatePackage,
+      currentPass,
+      newPass,
+      confirmPass,
+      updateInfo,
+    };
+  },
+};
+</script>

+ 126 - 0
hichina-main-front-mobile-first-v2/src/pages/WechatpayPage.vue

@@ -0,0 +1,126 @@
+<template>
+  <q-page>
+    <div class="row">
+      <div class="text-center text-h5 col-12" style="background-color: #e7e7e7">
+        Product Name: {{ productName }}
+      </div>
+    </div>
+    <div class="row justify-center" style="min-height: 500px">
+      <div
+        class="column shadow-7 rounded-borders"
+        style="
+          position: relative;
+          width: 340px;
+          max-width: 95%;
+          min-height: 450px;
+          height: 600;
+          background-color: yellow;
+          background-image: url('https://photoprism.hichinatravel.com/api/v1/t/d6f91f961d96953e9a61f331f328468caddd05b7/32mcf2k4/fit_2048');
+          background-size: cover;
+        "
+      >
+        <div class="col-2"></div>
+        <div class="col-2 row justify-center">
+          <div class="text-left col-7">Total Price: {{ price }}</div>
+        </div>
+        <div class="col row justify-center" v-if="renderComponent">
+          <div class="col-7">
+            <QRCodeVue3 :width="220" :height="220" :value="htmlContent" />
+          </div>
+        </div>
+      </div>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import QRCodeVue3 from "qrcode-vue3";
+import { onMounted, ref, nextTick, getCurrentInstance } from "vue";
+import { api } from "boot/axios";
+import { orderPaymentParamStore } from "stores/orderPaymentParamStore";
+import { useQuasar } from "quasar";
+export default {
+  name: "WechatpayPage",
+  components: {
+    QRCodeVue3: QRCodeVue3,
+  },
+  setup() {
+    const instance = getCurrentInstance();
+    const app = instance.appContext.app;
+    const gp = app.config.globalProperties;
+    const $q = useQuasar();
+
+    const oStore = orderPaymentParamStore();
+
+    const refreshIntervalId = ref("");
+
+    const codeUrl = ref("");
+    const orderId = ref("");
+    const price = ref("");
+    const productName = ref("");
+    const htmlContent = ref("");
+    const renderComponent = ref(true);
+
+    const forceRerender = async () => {
+      renderComponent.value = false;
+      // Wait for the change to get flushed to the DOM
+      await nextTick();
+
+      // Add the component back in
+      renderComponent.value = true;
+    };
+
+    function checkPaymentStatus() {
+      api
+        .get("/api/v1/order/wechatpaystatus/" + orderId.value)
+        .then((response) => {
+          console.log("status is:");
+          console.log(response.data);
+          if ("SUCCESS" == response.data) {
+            // jump to my order page
+            clearInterval(refreshIntervalId.value);
+            gp.$goPage("/my-orders");
+          } else {
+            forceRerender();
+          }
+        })
+        .catch((e) => {
+          console.log("Error getting /api/v1/order/wechatpaystatus");
+          console.log(e);
+        });
+    }
+
+    function b64_to_utf8(str) {
+      return window.atob(str);
+    }
+
+    onMounted(() => {
+      var allParamsFromPreviousPage = oStore.getPaymentDetail;
+      productName.value = allParamsFromPreviousPage.productName;
+      if (productName.value === "") {
+        gp.$goPage("/");
+        return;
+      }
+      console.log("what I got from book form:");
+      console.log(allParamsFromPreviousPage);
+      codeUrl.value = allParamsFromPreviousPage.codeUrl;
+      forceRerender();
+      console.log("decoded");
+      htmlContent.value = b64_to_utf8(codeUrl.value);
+      console.log(htmlContent.value);
+      orderId.value = allParamsFromPreviousPage.orderId;
+      price.value = allParamsFromPreviousPage.price;
+      refreshIntervalId.value = setInterval(() => {
+        checkPaymentStatus();
+      }, 3000);
+    });
+    return {
+      htmlContent,
+      productName,
+      price,
+      forceRerender,
+      renderComponent,
+    };
+  },
+};
+</script>

+ 30 - 0
hichina-main-front-mobile-first-v2/src/router/index.js

@@ -0,0 +1,30 @@
+import { route } from 'quasar/wrappers'
+import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
+import routes from './routes'
+
+/*
+ * If not building with SSR mode, you can
+ * directly export the Router instantiation;
+ *
+ * The function below can be async too; either use
+ * async/await or return a Promise which resolves
+ * with the Router instance.
+ */
+
+export default route(function (/* { store, ssrContext } */) {
+  const createHistory = process.env.SERVER
+    ? createMemoryHistory
+    : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
+
+  const Router = createRouter({
+    scrollBehavior: () => ({ left: 0, top: 0 }),
+    routes,
+
+    // Leave this as is and make changes in quasar.conf.js instead!
+    // quasar.conf.js -> build -> vueRouterMode
+    // quasar.conf.js -> build -> publicPath
+    history: createHistory(process.env.VUE_ROUTER_BASE)
+  })
+
+  return Router
+})

+ 197 - 0
hichina-main-front-mobile-first-v2/src/router/routes.js

@@ -0,0 +1,197 @@
+const routes = [
+  {
+    path: "/",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/IndexPage.vue"),
+        meta: { title: "HiChina Travel Home" },
+      },
+    ],
+  },
+  {
+    path: "/guideintro",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/GuideIntroPage.vue"),
+        meta: { title: "HiChina Travel Guidebook" },
+      },
+    ],
+  },
+  {
+    path: "/blog",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/BlogPage.vue"),
+        meta: { title: "HiChina Travel Blog" },
+      },
+    ],
+  },
+  {
+    path: "/product",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/TravelShopPage.vue"),
+        meta: { title: "HiChina Travel Shop" },
+      },
+    ],
+  },
+  {
+    path: "/destination",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/DestinationPage.vue"),
+        meta: { title: "HiChina Travel Destination" },
+      },
+    ],
+  },
+  {
+    path: "/auth/login",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/LoginPage.vue"),
+        meta: { title: "HiChina Travel Login" },
+      },
+    ],
+  },
+  {
+    path: "/auth/register",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/RegisterPage.vue"),
+        meta: { title: "HiChina Travel Register" },
+      },
+    ],
+  },
+  {
+    path: "/product-detail/:skuGroupId",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/ProductDetailPage.vue") },
+    ],
+  },
+  {
+    path: "/destination-detail/:destinationId",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/DestinationDetailPage.vue") },
+    ],
+  },
+  {
+    path: "/blog-detail/:blogId",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/BlogDetailPage.vue") },
+    ],
+  },
+  {
+    path: "/contact",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/ContactPage.vue"),
+        meta: { title: "HiChina Travel Contact" },
+      },
+    ],
+  },
+  {
+    path: "/my-blogs",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/MyBlogsPage.vue") }],
+  },
+  {
+    path: "/my-orders",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/MyOrdersPage.vue") }],
+  },
+  {
+    path: "/user-info",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/UserInfoPage.vue") }],
+  },
+  {
+    path: "/blog-create",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/BlogCreatePage.vue") },
+    ],
+  },
+  {
+    path: "/blog-edit/:blogId",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/BlogEditPage.vue") }],
+  },
+  {
+    path: "/order-detail/:orderId",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/OrderDetailPage.vue") },
+    ],
+  },
+  {
+    path: "/book",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/BookPage.vue") }],
+  },
+  {
+    path: "/alipay",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/AlipayPage.vue") }],
+  },
+  {
+    path: "/wechatpay",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/WechatpayPage.vue") },
+    ],
+  },
+  {
+    path: "/privacy",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [{ path: "", component: () => import("pages/PrivacyPage.vue") }],
+  },
+  {
+    path: "/finishpay",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/FinishpayPage.vue") },
+    ],
+  },
+  {
+    path: "/regvalidation/:regKey",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/RegvalidationPage.vue") },
+    ],
+  },
+  {
+    path: "/regsuccess",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      { path: "", component: () => import("pages/RegSuccessPage.vue") },
+    ],
+  },
+
+  // Always leave this as last one,
+  // but you can also remove it
+  {
+    path: "/:catchAll(.*)*",
+    component: () => import("pages/ErrorNotFound.vue"),
+  },
+];
+
+export default routes;

+ 17 - 0
hichina-main-front-mobile-first-v2/src/stores/bookParamStore.js

@@ -0,0 +1,17 @@
+import { defineStore } from "pinia";
+
+export const bookParamStore = defineStore("bookParam", {
+  state: () => ({
+    orderDetail: {},
+  }),
+
+  getters: {
+    getOrderDetail: (state) => state.orderDetail,
+  },
+
+  actions: {
+    setOrderDetail(val) {
+      this.orderDetail = val;
+    },
+  },
+});

+ 20 - 0
hichina-main-front-mobile-first-v2/src/stores/index.js

@@ -0,0 +1,20 @@
+import { store } from 'quasar/wrappers'
+import { createPinia } from 'pinia'
+
+/*
+ * If not building with SSR mode, you can
+ * directly export the Store instantiation;
+ *
+ * The function below can be async too; either use
+ * async/await or return a Promise which resolves
+ * with the Store instance.
+ */
+
+export default store((/* { ssrContext } */) => {
+  const pinia = createPinia()
+
+  // You can add Pinia plugins here
+  // pinia.use(SomePiniaPlugin)
+
+  return pinia
+})

+ 17 - 0
hichina-main-front-mobile-first-v2/src/stores/orderPaymentParamStore.js

@@ -0,0 +1,17 @@
+import { defineStore } from "pinia";
+
+export const orderPaymentParamStore = defineStore("orderPaymentParam", {
+  state: () => ({
+    paymentDetail: {},
+  }),
+
+  getters: {
+    getPaymentDetail: (state) => state.paymentDetail,
+  },
+
+  actions: {
+    setPaymentDetail(val) {
+      this.paymentDetail = val;
+    },
+  },
+});

+ 10 - 0
hichina-main-front-mobile-first-v2/src/stores/store-flag.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
+//  REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
+import "quasar/dist/types/feature-flag";
+
+declare module "quasar/dist/types/feature-flag" {
+  interface QuasarFeatureFlags {
+    store: true;
+  }
+}

+ 3432 - 0
hichina-main-front-mobile-first-v2/yarn.lock

@@ -0,0 +1,3432 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@aashutoshrathi/word-wrap@^1.2.3":
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
+  integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+
+"@babel/eslint-parser@^7.13.14":
+  version "7.22.15"
+  resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.15.tgz#263f059c476e29ca4972481a17b8b660cb025a34"
+  integrity sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==
+  dependencies:
+    "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
+    eslint-visitor-keys "^2.1.0"
+    semver "^6.3.1"
+
+"@babel/parser@^7.23.0":
+  version "7.23.0"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
+  integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
+
+"@babel/runtime@^7.12.5":
+  version "7.23.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
+  integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
+  dependencies:
+    regenerator-runtime "^0.14.0"
+
+"@esbuild/linux-loong64@0.14.54":
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
+  integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+  integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+  dependencies:
+    eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.6.1":
+  version "4.10.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
+  integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
+
+"@eslint/eslintrc@^2.1.2":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396"
+  integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^9.6.0"
+    globals "^13.19.0"
+    ignore "^5.2.0"
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
+    strip-json-comments "^3.1.1"
+
+"@eslint/js@8.52.0":
+  version "8.52.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c"
+  integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==
+
+"@humanwhocodes/config-array@^0.11.13":
+  version "0.11.13"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
+  integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==
+  dependencies:
+    "@humanwhocodes/object-schema" "^2.0.1"
+    debug "^4.1.1"
+    minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044"
+  integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==
+
+"@intlify/bundle-utils@^2.2.2":
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz#fe65ce2549a73b99b75f3e66209d741b4f4d61fd"
+  integrity sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==
+  dependencies:
+    "@intlify/message-compiler" "^9.1.0"
+    "@intlify/shared" "^9.1.0"
+    jsonc-eslint-parser "^1.0.1"
+    source-map "^0.6.1"
+    yaml-eslint-parser "^0.3.2"
+
+"@intlify/core-base@9.6.4":
+  version "9.6.4"
+  resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.6.4.tgz#f4c8c044f3a6d4a148b08189f404fcfcb9c923cb"
+  integrity sha512-NezA5NUpdlSPXTLCWzmd9xhLalfhL0sylqDjBUW9gF12t3jOtKYqqB5lD4FZbxkGdD/l+NMPitq44j8+i5D8UA==
+  dependencies:
+    "@intlify/message-compiler" "9.6.4"
+    "@intlify/shared" "9.6.4"
+
+"@intlify/message-compiler@9.6.4", "@intlify/message-compiler@^9.1.0":
+  version "9.6.4"
+  resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.6.4.tgz#e442286dd0d9d10bbd843d00743cc490cb142dfb"
+  integrity sha512-Ppkyh/dAw0Fq8cGSMQw5P1RgUDYNgmgM5y4d0JGw7Qgnbm085TeVDf82UdJtIk3iIbxrnR0jNUf1pNESnyHrPw==
+  dependencies:
+    "@intlify/shared" "9.6.4"
+    source-map-js "^1.0.2"
+
+"@intlify/shared@9.6.4", "@intlify/shared@^9.1.0":
+  version "9.6.4"
+  resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.6.4.tgz#109ee30ef35c05371da524e74fef58ae29d6bb41"
+  integrity sha512-dsmya7s6KK58UD60cDWqbpkQcllFpPB/f4dz8OqgK5Pe+CgeatKJjpuAG516DHXX0BtD0P5X6JCD45+1FMvvZw==
+
+"@intlify/vue-i18n-loader@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz#d7474e865721478f3754639963cf617bba825d62"
+  integrity sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==
+  dependencies:
+    "@intlify/bundle-utils" "^2.2.2"
+    "@intlify/shared" "^9.1.0"
+    js-yaml "^4.1.0"
+    json5 "^2.2.0"
+    loader-utils "^2.0.0"
+
+"@jridgewell/sourcemap-codec@^1.4.15":
+  version "1.4.15"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
+"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
+  version "5.1.1-v1"
+  resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
+  integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
+  dependencies:
+    eslint-scope "5.1.1"
+
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
+"@quasar/app-vite@^1.3.0":
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/@quasar/app-vite/-/app-vite-1.6.2.tgz#6aae131511dfdffafcaa4d87af31e40e53a67fce"
+  integrity sha512-keyJ28cqVtzNLDibQESo1c/DD++6+wqf8nQO6oMRqH6WgYcPs+YO1buRsRMXF9w3ghMCV0/9/Lj8Kyb7S55j6A==
+  dependencies:
+    "@quasar/render-ssr-error" "^1.0.1"
+    "@quasar/vite-plugin" "^1.3.3"
+    "@rollup/pluginutils" "^4.1.2"
+    "@types/chrome" "^0.0.208"
+    "@types/compression" "^1.7.2"
+    "@types/cordova" "0.0.34"
+    "@types/express" "^4.17.13"
+    "@vitejs/plugin-vue" "^2.2.0"
+    archiver "^5.3.0"
+    chokidar "^3.5.3"
+    ci-info "^3.7.1"
+    compression "^1.7.4"
+    cross-spawn "^7.0.3"
+    dot-prop "6.0.1"
+    elementtree "0.1.7"
+    esbuild "0.14.51"
+    express "^4.17.3"
+    fast-glob "3.2.12"
+    fs-extra "^11.1.0"
+    html-minifier "^4.0.0"
+    inquirer "^8.2.1"
+    isbinaryfile "^5.0.0"
+    kolorist "^1.5.1"
+    lodash "^4.17.21"
+    minimist "^1.2.6"
+    open "^8.4.0"
+    register-service-worker "^1.7.2"
+    rollup-plugin-visualizer "^5.5.4"
+    sass "1.32.12"
+    semver "^7.3.5"
+    serialize-javascript "^6.0.0"
+    table "^6.8.0"
+    vite "^2.9.13"
+    webpack-merge "^5.8.0"
+
+"@quasar/extras@^1.16.5":
+  version "1.16.7"
+  resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.16.7.tgz#9416689f2e55f594f1135383c35b9ab03875c522"
+  integrity sha512-nYF3gVE/si1YJ/D4qmAiHGwxoJIDCvTT8NI6ZmbTMPrur4J8xBKhfhfhyLoQ4k2jJZP6Rx0rUcB71FBNC2C8vQ==
+
+"@quasar/render-ssr-error@^1.0.1":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@quasar/render-ssr-error/-/render-ssr-error-1.0.2.tgz#92abb0d61cfdfbf51c2ec3cc2e4aadbf1c79f04f"
+  integrity sha512-Y0wyqYHVxc1IOBH6pRiKMSWDqO1mwQu11Zo8rw4cBdclPOQqFb7f65UuRbk5LfbqlXV2hYvklNcy0SBAOiAQnw==
+  dependencies:
+    stack-trace "^1.0.0-pre2"
+
+"@quasar/vite-plugin@^1.3.3":
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/@quasar/vite-plugin/-/vite-plugin-1.6.0.tgz#3b8f82656b14782fafe66b30dfac0775b87ab9dd"
+  integrity sha512-LmbV76G1CwWZbrEQhqyZpkRQTJyO3xpW55aXY1zWN+JhyUeG77CcMCEWteBVnJ6I6ehUPFDC9ONd2+WlwH6rNQ==
+
+"@rollup/pluginutils@^4.1.2":
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
+  integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
+  dependencies:
+    estree-walker "^2.0.1"
+    picomatch "^2.2.2"
+
+"@types/body-parser@*":
+  version "1.19.4"
+  resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.4.tgz#78ad68f1f79eb851aa3634db0c7f57f6f601b462"
+  integrity sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==
+  dependencies:
+    "@types/connect" "*"
+    "@types/node" "*"
+
+"@types/chrome@^0.0.208":
+  version "0.0.208"
+  resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.208.tgz#c52992e46723c783d3fd84a8b90dd8b3e87af67f"
+  integrity sha512-VDU/JnXkF5qaI7WBz14Azpa2VseZTgML0ia/g/B1sr9OfdOnHiH/zZ7P7qCDqxSlkqJh76/bPc8jLFcx8rHJmw==
+  dependencies:
+    "@types/filesystem" "*"
+    "@types/har-format" "*"
+
+"@types/compression@^1.7.2":
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/@types/compression/-/compression-1.7.4.tgz#c1af4a0f2d72ea6c96300ae58b9842aa87d77b6e"
+  integrity sha512-sdFVnQJRkQBX83ydsLCBm4A39p45y0QkxdAR689yOtAFNbbS9Acrp86RZWJj6BHRXyZH9tX4t1dU7XDiGdY3nA==
+  dependencies:
+    "@types/express" "*"
+
+"@types/connect@*":
+  version "3.4.37"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.37.tgz#c66a96689fd3127c8772eb3e9e5c6028ec1a9af5"
+  integrity sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==
+  dependencies:
+    "@types/node" "*"
+
+"@types/cordova@0.0.34":
+  version "0.0.34"
+  resolved "https://registry.yarnpkg.com/@types/cordova/-/cordova-0.0.34.tgz#ea7addf74ecec3d7629827a0c39e2c9addc73d04"
+  integrity sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA==
+
+"@types/express-serve-static-core@^4.17.33":
+  version "4.17.39"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz#2107afc0a4b035e6cb00accac3bdf2d76ae408c8"
+  integrity sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==
+  dependencies:
+    "@types/node" "*"
+    "@types/qs" "*"
+    "@types/range-parser" "*"
+    "@types/send" "*"
+
+"@types/express@*", "@types/express@^4.17.13":
+  version "4.17.20"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.20.tgz#e7c9b40276d29e38a4e3564d7a3d65911e2aa433"
+  integrity sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "^4.17.33"
+    "@types/qs" "*"
+    "@types/serve-static" "*"
+
+"@types/filesystem@*":
+  version "0.0.34"
+  resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.34.tgz#9b0d0d791ab6b217528cce8d391764b4b47607bf"
+  integrity sha512-La4bGrgck8/rosDUA1DJJP8hrFcKq0BV6JaaVlNnOo1rJdJDcft3//slEbAmsWNUJwXRCc0DXpeO40yuATlexw==
+  dependencies:
+    "@types/filewriter" "*"
+
+"@types/filewriter@*":
+  version "0.0.31"
+  resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.31.tgz#a5a256646bd98209baf9aa32073047f84f4c3f3f"
+  integrity sha512-12df1utOvPC80+UaVoOO1d81X8pa5MefHNS+gWX9R2ucSESpMz9K5QwlTWDGKASrzCpSFwj7NPYh+nTsolgEGA==
+
+"@types/har-format@*":
+  version "1.2.14"
+  resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.14.tgz#292e55d52be8659c8486316a0ae439760617e0a3"
+  integrity sha512-pEmBAoccWvO6XbSI8A7KvIDGEoKtlLWtdqVCKoVBcCDSFvR4Ijd7zGLu7MWGEqk2r8D54uWlMRt+VZuSrfFMzQ==
+
+"@types/http-errors@*":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.3.tgz#c54e61f79b3947d040f150abd58f71efb422ff62"
+  integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==
+
+"@types/mime@*":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.3.tgz#886674659ce55fe7c6c06ec5ca7c0eb276a08f91"
+  integrity sha512-i8MBln35l856k5iOhKk2XJ4SeAWg75mLIpZB4v6imOagKL6twsukBZGDMNhdOVk7yRFTMPpfILocMos59Q1otQ==
+
+"@types/mime@^1":
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.4.tgz#a4ed836e069491414bab92c31fdea9e557aca0d9"
+  integrity sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==
+
+"@types/node@*":
+  version "20.8.10"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.10.tgz#a5448b895c753ae929c26ce85cab557c6d4a365e"
+  integrity sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==
+  dependencies:
+    undici-types "~5.26.4"
+
+"@types/qs@*":
+  version "6.9.9"
+  resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.9.tgz#66f7b26288f6799d279edf13da7ccd40d2fa9197"
+  integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==
+
+"@types/range-parser@*":
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.6.tgz#7cb33992049fd7340d5b10c0098e104184dfcd2a"
+  integrity sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==
+
+"@types/send@*":
+  version "0.17.3"
+  resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.3.tgz#81b2ea5a3a18aad357405af2d643ccbe5a09020b"
+  integrity sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==
+  dependencies:
+    "@types/mime" "^1"
+    "@types/node" "*"
+
+"@types/serve-static@*":
+  version "1.15.4"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.4.tgz#44b5895a68ca637f06c229119e1c774ca88f81b2"
+  integrity sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==
+  dependencies:
+    "@types/http-errors" "*"
+    "@types/mime" "*"
+    "@types/node" "*"
+
+"@ungap/structured-clone@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
+  integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+
+"@unhead/dom@1.8.2":
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.8.2.tgz#84c9d90627492e9537e9334f1f5304234653076b"
+  integrity sha512-wyWf2bFItWvWHdfv8BlIyS7fcPYQBhJkwvkCjf3D7bnTXrd2ZyoUck07x3XApFiZavW8KD+A3S6jS39f/h4lBw==
+  dependencies:
+    "@unhead/schema" "1.8.2"
+    "@unhead/shared" "1.8.2"
+
+"@unhead/schema@1.8.2":
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.8.2.tgz#717e7e3876e502482863dce268524f9ac745c0dc"
+  integrity sha512-bpCv8ualep2aqbhkBXUKfhYICi35+Pb1CBc5v6oJHHJgHKBIAbBQpbpi14amRMnRuSYDeSOCEM6SV8OJntcNDA==
+  dependencies:
+    hookable "^5.5.3"
+    zhead "^2.2.4"
+
+"@unhead/shared@1.8.2":
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/@unhead/shared/-/shared-1.8.2.tgz#4536044d000bd829852ceaa539b5427830792fc3"
+  integrity sha512-GDrUDN3x2anpNQQOgjmKjpi2ygNsBAwok9C6Z1YCeM2YtjF1lhqF9cTCXaapNEq81FkC0R0LTgnVxa/HJ4n3lQ==
+  dependencies:
+    "@unhead/schema" "1.8.2"
+
+"@vitejs/plugin-vue@^2.2.0":
+  version "2.3.4"
+  resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e"
+  integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==
+
+"@vue/compiler-core@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.7.tgz#865a5734c971686d9737d85a0c5a08de045b6162"
+  integrity sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==
+  dependencies:
+    "@babel/parser" "^7.23.0"
+    "@vue/shared" "3.3.7"
+    estree-walker "^2.0.2"
+    source-map-js "^1.0.2"
+
+"@vue/compiler-dom@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz#a245aa03f9bfcdb537a239bf02842072de0644c9"
+  integrity sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==
+  dependencies:
+    "@vue/compiler-core" "3.3.7"
+    "@vue/shared" "3.3.7"
+
+"@vue/compiler-sfc@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz#219d04b3013c7b15fbc536e2279e07810b731cc2"
+  integrity sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==
+  dependencies:
+    "@babel/parser" "^7.23.0"
+    "@vue/compiler-core" "3.3.7"
+    "@vue/compiler-dom" "3.3.7"
+    "@vue/compiler-ssr" "3.3.7"
+    "@vue/reactivity-transform" "3.3.7"
+    "@vue/shared" "3.3.7"
+    estree-walker "^2.0.2"
+    magic-string "^0.30.5"
+    postcss "^8.4.31"
+    source-map-js "^1.0.2"
+
+"@vue/compiler-ssr@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz#eff4a70f7ceb800d60e68d208b96a030c0f1b636"
+  integrity sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==
+  dependencies:
+    "@vue/compiler-dom" "3.3.7"
+    "@vue/shared" "3.3.7"
+
+"@vue/devtools-api@^6.5.0":
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz#7f71f31e40973eeee65b9a64382b13593fdbd697"
+  integrity sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==
+
+"@vue/reactivity-transform@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz#eb9f5110af5085079b851d162205394bc790d539"
+  integrity sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==
+  dependencies:
+    "@babel/parser" "^7.23.0"
+    "@vue/compiler-core" "3.3.7"
+    "@vue/shared" "3.3.7"
+    estree-walker "^2.0.2"
+    magic-string "^0.30.5"
+
+"@vue/reactivity@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.3.7.tgz#48b6671a45ba33039da2c0eb25ae702f924486a9"
+  integrity sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==
+  dependencies:
+    "@vue/shared" "3.3.7"
+
+"@vue/runtime-core@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.3.7.tgz#c1eece1c98f936dc69dd0667d11b464579b128fd"
+  integrity sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==
+  dependencies:
+    "@vue/reactivity" "3.3.7"
+    "@vue/shared" "3.3.7"
+
+"@vue/runtime-dom@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz#e7cf88cc01591fdf6e3164825554fdadc3137ffc"
+  integrity sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==
+  dependencies:
+    "@vue/runtime-core" "3.3.7"
+    "@vue/shared" "3.3.7"
+    csstype "^3.1.2"
+
+"@vue/server-renderer@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.3.7.tgz#0cc3dc6ad39a54693e6e8f853caa3c7bb43b0364"
+  integrity sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==
+  dependencies:
+    "@vue/compiler-ssr" "3.3.7"
+    "@vue/shared" "3.3.7"
+
+"@vue/shared@3.3.7":
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.7.tgz#0091852fe5cc4237c8440fe32f3ab6bc920ae6d9"
+  integrity sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg==
+
+"@vueup/vue-quill@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@vueup/vue-quill/-/vue-quill-1.2.0.tgz#cd0d93559256d069f639723dd91c044e8162c72a"
+  integrity sha512-kd5QPSHMDpycklojPXno2Kw2JSiKMYduKYQckTm1RJoVDA557MnyUXgcuuDpry4HY/Rny9nGNcK+m3AHk94wag==
+  dependencies:
+    quill "^1.3.7"
+    quill-delta "^4.2.2"
+
+accepts@~1.3.5, accepts@~1.3.8:
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+  integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+  dependencies:
+    mime-types "~2.1.34"
+    negotiator "0.6.3"
+
+acorn-jsx@^5.2.0, acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^7.1.1, acorn@^7.4.1:
+  version "7.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+  integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
+acorn@^8.9.0:
+  version "8.11.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
+  integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
+
+ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+ajv@^8.0.1:
+  version "8.12.0"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
+  integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+    uri-js "^4.2.2"
+
+ansi-escapes@^4.2.1:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+  integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+  dependencies:
+    type-fest "^0.21.3"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+archiver-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
+  integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
+  dependencies:
+    glob "^7.1.4"
+    graceful-fs "^4.2.0"
+    lazystream "^1.0.0"
+    lodash.defaults "^4.2.0"
+    lodash.difference "^4.5.0"
+    lodash.flatten "^4.4.0"
+    lodash.isplainobject "^4.0.6"
+    lodash.union "^4.6.0"
+    normalize-path "^3.0.0"
+    readable-stream "^2.0.0"
+
+archiver-utils@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-3.0.4.tgz#a0d201f1cf8fce7af3b5a05aea0a337329e96ec7"
+  integrity sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==
+  dependencies:
+    glob "^7.2.3"
+    graceful-fs "^4.2.0"
+    lazystream "^1.0.0"
+    lodash.defaults "^4.2.0"
+    lodash.difference "^4.5.0"
+    lodash.flatten "^4.4.0"
+    lodash.isplainobject "^4.0.6"
+    lodash.union "^4.6.0"
+    normalize-path "^3.0.0"
+    readable-stream "^3.6.0"
+
+archiver@^5.3.0:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.2.tgz#99991d5957e53bd0303a392979276ac4ddccf3b0"
+  integrity sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==
+  dependencies:
+    archiver-utils "^2.1.0"
+    async "^3.2.4"
+    buffer-crc32 "^0.2.1"
+    readable-stream "^3.6.0"
+    readdir-glob "^1.1.2"
+    tar-stream "^2.2.0"
+    zip-stream "^4.1.0"
+
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-flatten@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+  integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
+
+astral-regex@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+  integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+async@^3.2.4:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+  integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+autoprefixer@^10.4.2:
+  version "10.4.16"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8"
+  integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==
+  dependencies:
+    browserslist "^4.21.10"
+    caniuse-lite "^1.0.30001538"
+    fraction.js "^4.3.6"
+    normalize-range "^0.1.2"
+    picocolors "^1.0.0"
+    postcss-value-parser "^4.2.0"
+
+axios@^1.4.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
+  integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
+  dependencies:
+    follow-redirects "^1.15.0"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
+babel-runtime@^6.11.6:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+big.js@^5.2.2:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
+  integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
+
+binary-extensions@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+  integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+bl@^4.0.3, bl@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+  integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+  dependencies:
+    buffer "^5.5.0"
+    inherits "^2.0.4"
+    readable-stream "^3.4.0"
+
+body-parser@1.20.1:
+  version "1.20.1"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
+  integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
+  dependencies:
+    bytes "3.1.2"
+    content-type "~1.0.4"
+    debug "2.6.9"
+    depd "2.0.0"
+    destroy "1.2.0"
+    http-errors "2.0.0"
+    iconv-lite "0.4.24"
+    on-finished "2.4.1"
+    qs "6.11.0"
+    raw-body "2.5.1"
+    type-is "~1.6.18"
+    unpipe "1.0.0"
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
+braces@^3.0.2, braces@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+browserslist@^4.21.10:
+  version "4.22.1"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619"
+  integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==
+  dependencies:
+    caniuse-lite "^1.0.30001541"
+    electron-to-chromium "^1.4.535"
+    node-releases "^2.0.13"
+    update-browserslist-db "^1.0.13"
+
+buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
+  version "0.2.13"
+  resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+  integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
+buffer@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
+bytes@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+  integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
+
+bytes@3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+  integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind@^1.0.0, call-bind@^1.0.2:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
+  integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==
+  dependencies:
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.1"
+    set-function-length "^1.1.1"
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camel-case@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+  integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==
+  dependencies:
+    no-case "^2.2.0"
+    upper-case "^1.1.1"
+
+caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541:
+  version "1.0.30001559"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz#95a982440d3d314c471db68d02664fb7536c5a30"
+  integrity sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chardet@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+  dependencies:
+    anymatch "~3.1.2"
+    braces "~3.0.2"
+    glob-parent "~5.1.2"
+    is-binary-path "~2.1.0"
+    is-glob "~4.0.1"
+    normalize-path "~3.0.0"
+    readdirp "~3.6.0"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+ci-info@^3.7.1:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+  integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+clean-css@^4.2.1:
+  version "4.2.4"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
+  integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==
+  dependencies:
+    source-map "~0.6.0"
+
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-spinners@^2.5.0:
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35"
+  integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==
+
+cli-width@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
+  integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+
+cliui@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+  integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+  dependencies:
+    string-width "^4.2.0"
+    strip-ansi "^6.0.1"
+    wrap-ansi "^7.0.0"
+
+clone-deep@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+  integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+  dependencies:
+    is-plain-object "^2.0.4"
+    kind-of "^6.0.2"
+    shallow-clone "^3.0.0"
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@^2.19.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+compress-commons@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.2.tgz#6542e59cb63e1f46a8b21b0e06f9a32e4c8b06df"
+  integrity sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==
+  dependencies:
+    buffer-crc32 "^0.2.13"
+    crc32-stream "^4.0.2"
+    normalize-path "^3.0.0"
+    readable-stream "^3.6.0"
+
+compressible@~2.0.16:
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+  integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
+  dependencies:
+    mime-db ">= 1.43.0 < 2"
+
+compression@^1.7.4:
+  version "1.7.4"
+  resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+  integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+  dependencies:
+    accepts "~1.3.5"
+    bytes "3.0.0"
+    compressible "~2.0.16"
+    debug "2.6.9"
+    on-headers "~1.0.2"
+    safe-buffer "5.1.2"
+    vary "~1.1.2"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+content-disposition@0.5.4:
+  version "0.5.4"
+  resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+  integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+  dependencies:
+    safe-buffer "5.2.1"
+
+content-type@~1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+  integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
+cookie-signature@1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+  integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
+
+cookie@0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
+  integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
+
+core-js@^2.4.0:
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
+  integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
+
+core-js@^3.6.5:
+  version "3.33.2"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.2.tgz#312bbf6996a3a517c04c99b9909cdd27138d1ceb"
+  integrity sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==
+
+core-util-is@~1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+  integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+crc-32@^1.2.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+  integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+
+crc32-stream@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.3.tgz#85dd677eb78fa7cad1ba17cc506a597d41fc6f33"
+  integrity sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==
+  dependencies:
+    crc-32 "^1.2.0"
+    readable-stream "^3.4.0"
+
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+csstype@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
+  integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
+
+debug@2.6.9:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
+deep-equal@^1.0.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
+  integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
+  dependencies:
+    is-arguments "^1.0.4"
+    is-date-object "^1.0.1"
+    is-regex "^1.0.4"
+    object-is "^1.0.1"
+    object-keys "^1.1.1"
+    regexp.prototype.flags "^1.2.0"
+
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+defaults@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
+  integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==
+  dependencies:
+    clone "^1.0.2"
+
+define-data-property@^1.0.1, define-data-property@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
+  integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==
+  dependencies:
+    get-intrinsic "^1.2.1"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.0"
+
+define-lazy-prop@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+  integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
+define-properties@^1.1.3, define-properties@^1.2.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
+  integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
+  dependencies:
+    define-data-property "^1.0.1"
+    has-property-descriptors "^1.0.0"
+    object-keys "^1.1.1"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+depd@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+  integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
+destroy@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+  integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+  dependencies:
+    esutils "^2.0.2"
+
+dot-prop@6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
+  integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
+  dependencies:
+    is-obj "^2.0.0"
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-to-chromium@^1.4.535:
+  version "1.4.575"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.575.tgz#7c0b87eb2c6214a993699792abd704de41255c39"
+  integrity sha512-kY2BGyvgAHiX899oF6xLXSIf99bAvvdPhDoJwG77nxCSyWYuRH6e9a9a3gpXBvCs6lj4dQZJkfnW2hdKWHEISg==
+
+elementtree@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0"
+  integrity sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==
+  dependencies:
+    sax "1.1.4"
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emojis-list@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
+  integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
+
+end-of-stream@^1.4.1:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+  dependencies:
+    once "^1.4.0"
+
+esbuild-android-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.51.tgz#414a087cb0de8db1e347ecca6c8320513de433db"
+  integrity sha512-6FOuKTHnC86dtrKDmdSj2CkcKF8PnqkaIXqvgydqfJmqBazCPdw+relrMlhGjkvVdiiGV70rpdnyFmA65ekBCQ==
+
+esbuild-android-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
+  integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
+
+esbuild-android-arm64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.51.tgz#55de3bce2aab72bcd2b606da4318ad00fb9c8151"
+  integrity sha512-vBtp//5VVkZWmYYvHsqBRCMMi1MzKuMIn5XDScmnykMTu9+TD9v0NMEDqQxvtFToeYmojdo5UCV2vzMQWJcJ4A==
+
+esbuild-android-arm64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
+  integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
+
+esbuild-darwin-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.51.tgz#4259f23ed6b4cea2ec8a28d87b7fb9801f093754"
+  integrity sha512-YFmXPIOvuagDcwCejMRtCDjgPfnDu+bNeh5FU2Ryi68ADDVlWEpbtpAbrtf/lvFTWPexbgyKgzppNgsmLPr8PA==
+
+esbuild-darwin-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
+  integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
+
+esbuild-darwin-arm64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.51.tgz#d77b4366a71d84e530ba019d540b538b295d494a"
+  integrity sha512-juYD0QnSKwAMfzwKdIF6YbueXzS6N7y4GXPDeDkApz/1RzlT42mvX9jgNmyOlWKN7YzQAYbcUEJmZJYQGdf2ow==
+
+esbuild-darwin-arm64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
+  integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
+
+esbuild-freebsd-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.51.tgz#27b6587b3639f10519c65e07219d249b01f2ad38"
+  integrity sha512-cLEI/aXjb6vo5O2Y8rvVSQ7smgLldwYY5xMxqh/dQGfWO+R1NJOFsiax3IS4Ng300SVp7Gz3czxT6d6qf2cw0g==
+
+esbuild-freebsd-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
+  integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
+
+esbuild-freebsd-arm64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.51.tgz#63c435917e566808c71fafddc600aca4d78be1ec"
+  integrity sha512-TcWVw/rCL2F+jUgRkgLa3qltd5gzKjIMGhkVybkjk6PJadYInPtgtUBp1/hG+mxyigaT7ib+od1Xb84b+L+1Mg==
+
+esbuild-freebsd-arm64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
+  integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
+
+esbuild-linux-32@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.51.tgz#c3da774143a37e7f11559b9369d98f11f997a5d9"
+  integrity sha512-RFqpyC5ChyWrjx8Xj2K0EC1aN0A37H6OJfmUXIASEqJoHcntuV3j2Efr9RNmUhMfNE6yEj2VpYuDteZLGDMr0w==
+
+esbuild-linux-32@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
+  integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
+
+esbuild-linux-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.51.tgz#5d92b67f674e02ae0b4a9de9a757ba482115c4ae"
+  integrity sha512-dxjhrqo5i7Rq6DXwz5v+MEHVs9VNFItJmHBe1CxROWNf4miOGoQhqSG8StStbDkQ1Mtobg6ng+4fwByOhoQoeA==
+
+esbuild-linux-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
+  integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
+
+esbuild-linux-arm64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.51.tgz#dac84740516e859d8b14e1ecc478dd5241b10c93"
+  integrity sha512-D9rFxGutoqQX3xJPxqd6o+kvYKeIbM0ifW2y0bgKk5HPgQQOo2k9/2Vpto3ybGYaFPCE5qTGtqQta9PoP6ZEzw==
+
+esbuild-linux-arm64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
+  integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
+
+esbuild-linux-arm@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.51.tgz#b3ae7000696cd53ed95b2b458554ff543a60e106"
+  integrity sha512-LsJynDxYF6Neg7ZC7748yweCDD+N8ByCv22/7IAZglIEniEkqdF4HCaa49JNDLw1UQGlYuhOB8ZT/MmcSWzcWg==
+
+esbuild-linux-arm@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
+  integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
+
+esbuild-linux-mips64le@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.51.tgz#dad10770fac94efa092b5a0643821c955a9dd385"
+  integrity sha512-vS54wQjy4IinLSlb5EIlLoln8buh1yDgliP4CuEHumrPk4PvvP4kTRIG4SzMXm6t19N0rIfT4bNdAxzJLg2k6A==
+
+esbuild-linux-mips64le@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
+  integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
+
+esbuild-linux-ppc64le@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.51.tgz#b68c2f8294d012a16a88073d67e976edd4850ae0"
+  integrity sha512-xcdd62Y3VfGoyphNP/aIV9LP+RzFw5M5Z7ja+zdpQHHvokJM7d0rlDRMN+iSSwvUymQkqZO+G/xjb4/75du8BQ==
+
+esbuild-linux-ppc64le@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
+  integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
+
+esbuild-linux-riscv64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.51.tgz#608a318b8697123e44c1e185cdf6708e3df50b93"
+  integrity sha512-syXHGak9wkAnFz0gMmRBoy44JV0rp4kVCEA36P5MCeZcxFq8+fllBC2t6sKI23w3qd8Vwo9pTADCgjTSf3L3rA==
+
+esbuild-linux-riscv64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
+  integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
+
+esbuild-linux-s390x@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.51.tgz#c9e7791170a3295dba79b93aa452beb9838a8625"
+  integrity sha512-kFAJY3dv+Wq8o28K/C7xkZk/X34rgTwhknSsElIqoEo8armCOjMJ6NsMxm48KaWY2h2RUYGtQmr+RGuUPKBhyw==
+
+esbuild-linux-s390x@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
+  integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
+
+esbuild-netbsd-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.51.tgz#0abd40b8c2e37fda6f5cc41a04cb2b690823d891"
+  integrity sha512-ZZBI7qrR1FevdPBVHz/1GSk1x5GDL/iy42Zy8+neEm/HA7ma+hH/bwPEjeHXKWUDvM36CZpSL/fn1/y9/Hb+1A==
+
+esbuild-netbsd-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
+  integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
+
+esbuild-openbsd-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.51.tgz#4adba0b7ea7eb1428bb00d8e94c199a949b130e8"
+  integrity sha512-7R1/p39M+LSVQVgDVlcY1KKm6kFKjERSX1lipMG51NPcspJD1tmiZSmmBXoY5jhHIu6JL1QkFDTx94gMYK6vfA==
+
+esbuild-openbsd-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
+  integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
+
+esbuild-sunos-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.51.tgz#4b8a6d97dfedda30a6e39607393c5c90ebf63891"
+  integrity sha512-HoHaCswHxLEYN8eBTtyO0bFEWvA3Kdb++hSQ/lLG7TyKF69TeSG0RNoBRAs45x/oCeWaTDntEZlYwAfQlhEtJA==
+
+esbuild-sunos-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
+  integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
+
+esbuild-windows-32@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.51.tgz#d31d8ca0c1d314fb1edea163685a423b62e9ac17"
+  integrity sha512-4rtwSAM35A07CBt1/X8RWieDj3ZUHQqUOaEo5ZBs69rt5WAFjP4aqCIobdqOy4FdhYw1yF8Z0xFBTyc9lgPtEg==
+
+esbuild-windows-32@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
+  integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
+
+esbuild-windows-64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.51.tgz#7d3c09c8652d222925625637bdc7e6c223e0085d"
+  integrity sha512-HoN/5HGRXJpWODprGCgKbdMvrC3A2gqvzewu2eECRw2sYxOUoh2TV1tS+G7bHNapPGI79woQJGV6pFH7GH7qnA==
+
+esbuild-windows-64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
+  integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
+
+esbuild-windows-arm64@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.51.tgz#0220d2304bfdc11bc27e19b2aaf56edf183e4ae9"
+  integrity sha512-JQDqPjuOH7o+BsKMSddMfmVJXrnYZxXDHsoLHc0xgmAZkOOCflRmC43q31pk79F9xuyWY45jDBPolb5ZgGOf9g==
+
+esbuild-windows-arm64@0.14.54:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
+  integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
+
+esbuild@0.14.51:
+  version "0.14.51"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.51.tgz#1c8ecbc8db3710da03776211dc3ee3448f7aa51e"
+  integrity sha512-+CvnDitD7Q5sT7F+FM65sWkF8wJRf+j9fPcprxYV4j+ohmzVj2W7caUqH2s5kCaCJAfcAICjSlKhDCcvDpU7nw==
+  optionalDependencies:
+    esbuild-android-64 "0.14.51"
+    esbuild-android-arm64 "0.14.51"
+    esbuild-darwin-64 "0.14.51"
+    esbuild-darwin-arm64 "0.14.51"
+    esbuild-freebsd-64 "0.14.51"
+    esbuild-freebsd-arm64 "0.14.51"
+    esbuild-linux-32 "0.14.51"
+    esbuild-linux-64 "0.14.51"
+    esbuild-linux-arm "0.14.51"
+    esbuild-linux-arm64 "0.14.51"
+    esbuild-linux-mips64le "0.14.51"
+    esbuild-linux-ppc64le "0.14.51"
+    esbuild-linux-riscv64 "0.14.51"
+    esbuild-linux-s390x "0.14.51"
+    esbuild-netbsd-64 "0.14.51"
+    esbuild-openbsd-64 "0.14.51"
+    esbuild-sunos-64 "0.14.51"
+    esbuild-windows-32 "0.14.51"
+    esbuild-windows-64 "0.14.51"
+    esbuild-windows-arm64 "0.14.51"
+
+esbuild@^0.14.27:
+  version "0.14.54"
+  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
+  integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
+  optionalDependencies:
+    "@esbuild/linux-loong64" "0.14.54"
+    esbuild-android-64 "0.14.54"
+    esbuild-android-arm64 "0.14.54"
+    esbuild-darwin-64 "0.14.54"
+    esbuild-darwin-arm64 "0.14.54"
+    esbuild-freebsd-64 "0.14.54"
+    esbuild-freebsd-arm64 "0.14.54"
+    esbuild-linux-32 "0.14.54"
+    esbuild-linux-64 "0.14.54"
+    esbuild-linux-arm "0.14.54"
+    esbuild-linux-arm64 "0.14.54"
+    esbuild-linux-mips64le "0.14.54"
+    esbuild-linux-ppc64le "0.14.54"
+    esbuild-linux-riscv64 "0.14.54"
+    esbuild-linux-s390x "0.14.54"
+    esbuild-netbsd-64 "0.14.54"
+    esbuild-openbsd-64 "0.14.54"
+    esbuild-sunos-64 "0.14.54"
+    esbuild-windows-32 "0.14.54"
+    esbuild-windows-64 "0.14.54"
+    esbuild-windows-arm64 "0.14.54"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-config-prettier@^8.1.0:
+  version "8.10.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11"
+  integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==
+
+eslint-plugin-vue@^9.0.0:
+  version "9.18.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.18.1.tgz#73cf29df7450ce5913296465f8d1dc545344920c"
+  integrity sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    natural-compare "^1.4.0"
+    nth-check "^2.1.1"
+    postcss-selector-parser "^6.0.13"
+    semver "^7.5.4"
+    vue-eslint-parser "^9.3.1"
+    xml-name-validator "^4.0.0"
+
+eslint-scope@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
+
+eslint-scope@^7.1.1, eslint-scope@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
+  integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+  integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+  dependencies:
+    eslint-visitor-keys "^1.1.0"
+
+eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint-visitor-keys@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+  integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint@^8.10.0:
+  version "8.52.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc"
+  integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.6.1"
+    "@eslint/eslintrc" "^2.1.2"
+    "@eslint/js" "8.52.0"
+    "@humanwhocodes/config-array" "^0.11.13"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@nodelib/fs.walk" "^1.2.8"
+    "@ungap/structured-clone" "^1.2.0"
+    ajv "^6.12.4"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.3.2"
+    doctrine "^3.0.0"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^7.2.2"
+    eslint-visitor-keys "^3.4.3"
+    espree "^9.6.1"
+    esquery "^1.4.2"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^6.0.1"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    globals "^13.19.0"
+    graphemer "^1.4.0"
+    ignore "^5.2.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    is-path-inside "^3.0.3"
+    js-yaml "^4.1.0"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
+    natural-compare "^1.4.0"
+    optionator "^0.9.3"
+    strip-ansi "^6.0.1"
+    text-table "^0.2.0"
+
+espree@^6.0.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
+  integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==
+  dependencies:
+    acorn "^7.1.1"
+    acorn-jsx "^5.2.0"
+    eslint-visitor-keys "^1.1.0"
+
+espree@^9.3.1, espree@^9.6.0, espree@^9.6.1:
+  version "9.6.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+  integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+  dependencies:
+    acorn "^8.9.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.4.1"
+
+esquery@^1.4.0, esquery@^1.4.2:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+  integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+estree-walker@^2.0.1, estree-walker@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+
+eventemitter3@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
+  integrity sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==
+
+express@^4.17.3:
+  version "4.18.2"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
+  integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
+  dependencies:
+    accepts "~1.3.8"
+    array-flatten "1.1.1"
+    body-parser "1.20.1"
+    content-disposition "0.5.4"
+    content-type "~1.0.4"
+    cookie "0.5.0"
+    cookie-signature "1.0.6"
+    debug "2.6.9"
+    depd "2.0.0"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    finalhandler "1.2.0"
+    fresh "0.5.2"
+    http-errors "2.0.0"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "2.4.1"
+    parseurl "~1.3.3"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.7"
+    qs "6.11.0"
+    range-parser "~1.2.1"
+    safe-buffer "5.2.1"
+    send "0.18.0"
+    serve-static "1.15.0"
+    setprototypeof "1.2.0"
+    statuses "2.0.1"
+    type-is "~1.6.18"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
+extend@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+external-editor@^3.0.3:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+  integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+  dependencies:
+    chardet "^0.7.0"
+    iconv-lite "^0.4.24"
+    tmp "^0.0.33"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-diff@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154"
+  integrity sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==
+
+fast-diff@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+  integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
+fast-glob@3.2.12:
+  version "3.2.12"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
+  integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.2"
+    merge2 "^1.3.0"
+    micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
+  integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
+  dependencies:
+    reusify "^1.0.4"
+
+figures@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+  integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
+file-entry-cache@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+  integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+  dependencies:
+    flat-cache "^3.0.4"
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+finalhandler@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
+  integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
+  dependencies:
+    debug "2.6.9"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    on-finished "2.4.1"
+    parseurl "~1.3.3"
+    statuses "2.0.1"
+    unpipe "~1.0.0"
+
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.1.tgz#a02a15fdec25a8f844ff7cc658f03dd99eb4609b"
+  integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==
+  dependencies:
+    flatted "^3.2.9"
+    keyv "^4.5.3"
+    rimraf "^3.0.2"
+
+flat@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
+  integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+
+flatted@^3.2.9:
+  version "3.2.9"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
+  integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
+
+follow-redirects@^1.15.0:
+  version "1.15.3"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
+  integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
+
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
+forwarded@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+  integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fraction.js@^4.3.6:
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
+  integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
+
+fs-constants@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+  integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
+fs-extra@^11.1.0:
+  version "11.1.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d"
+  integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+functions-have-names@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+  integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
+
+get-caller-file@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b"
+  integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==
+  dependencies:
+    function-bind "^1.1.2"
+    has-proto "^1.0.1"
+    has-symbols "^1.0.3"
+    hasown "^2.0.0"
+
+glob-parent@^5.1.2, glob-parent@~5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
+glob@^7.1.3, glob@^7.1.4, glob@^7.2.3:
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.1.1"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^13.19.0:
+  version "13.23.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02"
+  integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==
+  dependencies:
+    type-fest "^0.20.2"
+
+gopd@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+  dependencies:
+    get-intrinsic "^1.1.3"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+graphemer@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+  integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
+  integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
+  dependencies:
+    get-intrinsic "^1.2.2"
+
+has-proto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
+  integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
+
+has-symbols@^1.0.2, has-symbols@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+  integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+  integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
+  dependencies:
+    has-symbols "^1.0.2"
+
+hasown@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
+  integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
+  dependencies:
+    function-bind "^1.1.2"
+
+he@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+hookable@^5.5.3:
+  version "5.5.3"
+  resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d"
+  integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==
+
+html-minifier@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
+  integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==
+  dependencies:
+    camel-case "^3.0.0"
+    clean-css "^4.2.1"
+    commander "^2.19.0"
+    he "^1.2.0"
+    param-case "^2.1.1"
+    relateurl "^0.2.7"
+    uglify-js "^3.5.1"
+
+http-errors@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+  integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+  dependencies:
+    depd "2.0.0"
+    inherits "2.0.4"
+    setprototypeof "1.2.0"
+    statuses "2.0.1"
+    toidentifier "1.0.1"
+
+iconv-lite@0.4.24, iconv-lite@^0.4.24:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+ieee754@^1.1.13:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+ignore@^5.2.0:
+  version "5.2.4"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
+  integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inquirer@^8.2.1:
+  version "8.2.6"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562"
+  integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==
+  dependencies:
+    ansi-escapes "^4.2.1"
+    chalk "^4.1.1"
+    cli-cursor "^3.1.0"
+    cli-width "^3.0.0"
+    external-editor "^3.0.3"
+    figures "^3.0.0"
+    lodash "^4.17.21"
+    mute-stream "0.0.8"
+    ora "^5.4.1"
+    run-async "^2.4.0"
+    rxjs "^7.5.5"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+    through "^2.3.6"
+    wrap-ansi "^6.0.1"
+
+ipaddr.js@1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-arguments@^1.0.4:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+  integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
+is-binary-path@~2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+  dependencies:
+    binary-extensions "^2.0.0"
+
+is-core-module@^2.13.0:
+  version "2.13.1"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
+  integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+  dependencies:
+    hasown "^2.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+  integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-docker@^2.0.0, is-docker@^2.1.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+  integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-interactive@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+  integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-obj@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-path-inside@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-regex@^1.0.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+  integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
+is-unicode-supported@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+  integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+is-wsl@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+  integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+  dependencies:
+    is-docker "^2.0.0"
+
+isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isbinaryfile@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.0.tgz#034b7e54989dab8986598cbcea41f66663c65234"
+  integrity sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^2.1.2, json5@^2.2.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+jsonc-eslint-parser@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz#8cbe99f6f5199acbc5a823c4c0b6135411027fa6"
+  integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==
+  dependencies:
+    acorn "^7.4.1"
+    eslint-utils "^2.1.0"
+    eslint-visitor-keys "^1.3.0"
+    espree "^6.0.0"
+    semver "^6.3.0"
+
+jsonfile@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+  dependencies:
+    universalify "^2.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+keyv@^4.5.3:
+  version "4.5.4"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+  integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+  dependencies:
+    json-buffer "3.0.1"
+
+kind-of@^6.0.2:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+  integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+kolorist@^1.5.1:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
+  integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
+
+lazystream@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
+  integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==
+  dependencies:
+    readable-stream "^2.0.5"
+
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+loader-utils@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
+  integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+  integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
+lodash.defaults@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+  integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
+
+lodash.difference@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
+  integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==
+
+lodash.flatten@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+  integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
+
+lodash.isequal@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+  integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+
+lodash.isplainobject@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+  integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.truncate@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
+  integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
+
+lodash.union@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
+  integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==
+
+lodash@^4.17.20, lodash@^4.17.21:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+  integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+  dependencies:
+    chalk "^4.1.0"
+    is-unicode-supported "^0.1.0"
+
+lower-case@^1.1.1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+  integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+magic-string@^0.30.5:
+  version "0.30.5"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9"
+  integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.4.15"
+
+media-typer@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+  integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+
+merge-descriptors@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+  integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
+
+merge2@^1.3.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+methods@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+  integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
+
+micromatch@^4.0.4:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+  version "1.52.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
+  version "2.1.35"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
+mime@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimatch@^5.1.0:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+minimist@^1.2.6:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
+ms@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mute-stream@0.0.8:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+  integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
+nanoid@^3.3.6:
+  version "3.3.6"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
+  integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+negotiator@0.6.3:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+  integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+no-case@^2.2.0:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+  integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+  dependencies:
+    lower-case "^1.1.1"
+
+node-releases@^2.0.13:
+  version "2.0.13"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
+  integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
+
+nth-check@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+object-inspect@^1.9.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
+  integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
+
+object-is@^1.0.1:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
+  integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+
+object-keys@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+on-finished@2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+  integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+  dependencies:
+    ee-first "1.1.1"
+
+on-headers@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+  integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+  dependencies:
+    wrappy "1"
+
+onetime@^5.1.0:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+open@^8.4.0:
+  version "8.4.2"
+  resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
+  integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==
+  dependencies:
+    define-lazy-prop "^2.0.0"
+    is-docker "^2.1.1"
+    is-wsl "^2.2.0"
+
+optionator@^0.9.3:
+  version "0.9.3"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+  integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+  dependencies:
+    "@aashutoshrathi/word-wrap" "^1.2.3"
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+
+ora@^5.4.1:
+  version "5.4.1"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
+  integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+  dependencies:
+    bl "^4.1.0"
+    chalk "^4.1.0"
+    cli-cursor "^3.1.0"
+    cli-spinners "^2.5.0"
+    is-interactive "^1.0.0"
+    is-unicode-supported "^0.1.0"
+    log-symbols "^4.1.0"
+    strip-ansi "^6.0.0"
+    wcwidth "^1.0.1"
+
+os-tmpdir@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+  integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+
+p-limit@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
+param-case@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+  integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==
+  dependencies:
+    no-case "^2.2.0"
+
+parchment@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/parchment/-/parchment-1.1.4.tgz#aeded7ab938fe921d4c34bc339ce1168bc2ffde5"
+  integrity sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-to-regexp@0.1.7:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+  integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pinia@^2.1.4:
+  version "2.1.7"
+  resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.1.7.tgz#4cf5420d9324ca00b7b4984d3fbf693222115bbc"
+  integrity sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==
+  dependencies:
+    "@vue/devtools-api" "^6.5.0"
+    vue-demi ">=0.14.5"
+
+postcss-selector-parser@^6.0.13:
+  version "6.0.13"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b"
+  integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==
+  dependencies:
+    cssesc "^3.0.0"
+    util-deprecate "^1.0.2"
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.4.13, postcss@^8.4.14, postcss@^8.4.31:
+  version "8.4.31"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
+  integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
+  dependencies:
+    nanoid "^3.3.6"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier@^2.5.1:
+  version "2.8.8"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
+  integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
+
+prismjs@^1.22.0:
+  version "1.29.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
+  integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+proxy-addr@~2.0.7:
+  version "2.0.7"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+  integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
+  dependencies:
+    forwarded "0.2.0"
+    ipaddr.js "1.9.1"
+
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
+punycode@^2.1.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+  integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
+qrcode-generator@^1.4.4:
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7"
+  integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==
+
+qrcode-vue3@^1.6.8:
+  version "1.6.8"
+  resolved "https://registry.yarnpkg.com/qrcode-vue3/-/qrcode-vue3-1.6.8.tgz#66f86f7a45ab3044043b8447ec1b2ebea630d53b"
+  integrity sha512-LtMnwKWi58ZqHbXBcsTF/VxDYhI6RrBIrDQw8fbDVlO8p5tJBZa7TaIaVYLY937vKO2WCEBmOKksGlpm5ccEIg==
+  dependencies:
+    qrcode-generator "^1.4.4"
+
+qs@6.11.0:
+  version "6.11.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
+  integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
+  dependencies:
+    side-channel "^1.0.4"
+
+quasar@^2.6.0:
+  version "2.13.0"
+  resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.13.0.tgz#2b156770137537f87fa2c23df7fbd4858d9db2ed"
+  integrity sha512-VPxbDy4vsobROUqGfet415SAvthj03fbm06pHSOYH5TeixWKHiLBIbwV9/LmWsDqwEsmMpADkHNZhmBZ40ha2w==
+
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+quill-delta@^3.6.2:
+  version "3.6.3"
+  resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"
+  integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==
+  dependencies:
+    deep-equal "^1.0.1"
+    extend "^3.0.2"
+    fast-diff "1.1.2"
+
+quill-delta@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-4.2.2.tgz#015397d046e0a3bed087cd8a51f98c11a1b8f351"
+  integrity sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==
+  dependencies:
+    fast-diff "1.2.0"
+    lodash.clonedeep "^4.5.0"
+    lodash.isequal "^4.5.0"
+
+quill-image-uploader@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/quill-image-uploader/-/quill-image-uploader-1.3.0.tgz#cf5396e3bf792c350c946be2e592eea2a0b14c8f"
+  integrity sha512-vO43GEn93rGThje/MlotkQE9OV5nOKBZ4oKhn71L/EjrM/J2P/8iDDVd9GEwlsGsbskeJqPLopsSQ4HlVVIn6A==
+
+quill@^1.3.7:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8"
+  integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==
+  dependencies:
+    clone "^2.1.1"
+    deep-equal "^1.0.1"
+    eventemitter3 "^2.0.3"
+    extend "^3.0.2"
+    parchment "^1.1.4"
+    quill-delta "^3.6.2"
+
+randombytes@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+range-parser@~1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.5.1:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
+  integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
+  dependencies:
+    bytes "3.1.2"
+    http-errors "2.0.0"
+    iconv-lite "0.4.24"
+    unpipe "1.0.0"
+
+readable-stream@^2.0.0, readable-stream@^2.0.5:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
+  integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+readdir-glob@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584"
+  integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==
+  dependencies:
+    minimatch "^5.1.0"
+
+readdirp@~3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+  integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+  dependencies:
+    picomatch "^2.2.1"
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-runtime@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
+  integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
+
+regexp.prototype.flags@^1.2.0:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e"
+  integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.2.0"
+    set-function-name "^2.0.0"
+
+register-service-worker@^1.7.2:
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.7.2.tgz#6516983e1ef790a98c4225af1216bc80941a4bd2"
+  integrity sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==
+
+relateurl@^0.2.7:
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+  integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+require-from-string@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+  integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve@^1.22.0:
+  version "1.22.8"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+  integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+  dependencies:
+    is-core-module "^2.13.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
+rollup-plugin-visualizer@^5.5.4:
+  version "5.9.2"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.9.2.tgz#f1aa2d9b1be8ebd6869223c742324897464d8891"
+  integrity sha512-waHktD5mlWrYFrhOLbti4YgQCn1uR24nYsNuXxg7LkPH8KdTXVWR9DNY1WU0QqokyMixVXJS4J04HNrVTMP01A==
+  dependencies:
+    open "^8.4.0"
+    picomatch "^2.3.1"
+    source-map "^0.7.4"
+    yargs "^17.5.1"
+
+"rollup@>=2.59.0 <2.78.0":
+  version "2.77.3"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
+  integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+run-async@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+  integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
+
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
+rxjs@^7.5.5:
+  version "7.8.1"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
+  integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
+  dependencies:
+    tslib "^2.1.0"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sass@1.32.12:
+  version "1.32.12"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.12.tgz#a2a47ad0f1c168222db5206444a30c12457abb9f"
+  integrity sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==
+  dependencies:
+    chokidar ">=3.0.0 <4.0.0"
+
+sax@1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9"
+  integrity sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==
+
+semver@^6.3.0, semver@^6.3.1:
+  version "6.3.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+  integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.3.5, semver@^7.3.6, semver@^7.5.4:
+  version "7.5.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+  integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
+  dependencies:
+    lru-cache "^6.0.0"
+
+send@0.18.0:
+  version "0.18.0"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+  integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
+  dependencies:
+    debug "2.6.9"
+    depd "2.0.0"
+    destroy "1.2.0"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    fresh "0.5.2"
+    http-errors "2.0.0"
+    mime "1.6.0"
+    ms "2.1.3"
+    on-finished "2.4.1"
+    range-parser "~1.2.1"
+    statuses "2.0.1"
+
+serialize-javascript@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c"
+  integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==
+  dependencies:
+    randombytes "^2.1.0"
+
+serve-static@1.15.0:
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
+  integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.3"
+    send "0.18.0"
+
+set-function-length@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
+  integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==
+  dependencies:
+    define-data-property "^1.1.1"
+    get-intrinsic "^1.2.1"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.0"
+
+set-function-name@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a"
+  integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==
+  dependencies:
+    define-data-property "^1.0.1"
+    functions-have-names "^1.2.3"
+    has-property-descriptors "^1.0.0"
+
+setprototypeof@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+  integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shallow-clone@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+  integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+  dependencies:
+    kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+side-channel@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+  integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+  dependencies:
+    call-bind "^1.0.0"
+    get-intrinsic "^1.0.2"
+    object-inspect "^1.9.0"
+
+signal-exit@^3.0.2:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+slice-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+  integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
+source-map-js@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@^0.6.1, source-map@~0.6.0:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.7.4:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
+  integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
+
+stack-trace@^1.0.0-pre2:
+  version "1.0.0-pre2"
+  resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-1.0.0-pre2.tgz#46a83a79f1b287807e9aaafc6a5dd8bcde626f9c"
+  integrity sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==
+
+statuses@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+  integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+table@^6.8.0:
+  version "6.8.1"
+  resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf"
+  integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==
+  dependencies:
+    ajv "^8.0.1"
+    lodash.truncate "^4.4.2"
+    slice-ansi "^4.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
+
+tar-stream@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+  integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+  dependencies:
+    bl "^4.0.3"
+    end-of-stream "^1.4.1"
+    fs-constants "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^3.1.1"
+
+text-table@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+  dependencies:
+    os-tmpdir "~1.0.2"
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+toidentifier@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+  integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+tslib@^2.1.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+type-fest@^0.21.3:
+  version "0.21.3"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+  integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+type-is@~1.6.18:
+  version "1.6.18"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.24"
+
+uglify-js@^3.5.1:
+  version "3.17.4"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c"
+  integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==
+
+undici-types@~5.26.4:
+  version "5.26.5"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+  integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
+unhead@^1.1.30:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/unhead/-/unhead-1.8.2.tgz#97598439deaec7efb987cc6818b51a25f980c1fd"
+  integrity sha512-f7Ha07cT+cgYav06tRNKxUrOZ722QtvYExn0McE68DYUGUM2boPCxXWlHcZXpSAbOj5OQI5AwQE5Xb3Qp2dWDQ==
+  dependencies:
+    "@unhead/dom" "1.8.2"
+    "@unhead/schema" "1.8.2"
+    "@unhead/shared" "1.8.2"
+    hookable "^5.5.3"
+
+universalify@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+  integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+  integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
+update-browserslist-db@^1.0.13:
+  version "1.0.13"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
+  integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+upper-case@^1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+  integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utils-merge@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+  integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+
+vary@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+  integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+
+vite@^2.9.13:
+  version "2.9.16"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.16.tgz#daf7ba50f5cc37a7bf51b118ba06bc36e97898e9"
+  integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==
+  dependencies:
+    esbuild "^0.14.27"
+    postcss "^8.4.13"
+    resolve "^1.22.0"
+    rollup ">=2.59.0 <2.78.0"
+  optionalDependencies:
+    fsevents "~2.3.2"
+
+vue-demi@>=0.14.5:
+  version "0.14.6"
+  resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.6.tgz#dc706582851dc1cdc17a0054f4fec2eb6df74c92"
+  integrity sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==
+
+vue-eslint-parser@^9.3.1:
+  version "9.3.2"
+  resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz#6f9638e55703f1c77875a19026347548d93fd499"
+  integrity sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==
+  dependencies:
+    debug "^4.3.4"
+    eslint-scope "^7.1.1"
+    eslint-visitor-keys "^3.3.0"
+    espree "^9.3.1"
+    esquery "^1.4.0"
+    lodash "^4.17.21"
+    semver "^7.3.6"
+
+vue-i18n@^9.0.0, vue-i18n@^9.3.0-beta.24:
+  version "9.6.4"
+  resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.6.4.tgz#6ca3eee9073773e31cf1987e8eedc5ec1d5dcd78"
+  integrity sha512-xYoUk0ya6RoTjXtR0w4aHrzPT3CbAZfBMcDj2IHUkP+ddpp1V7M4KYBBHwlHncEapf/skm0BoXOIe7RJ5d/Epg==
+  dependencies:
+    "@intlify/core-base" "9.6.4"
+    "@intlify/shared" "9.6.4"
+    "@vue/devtools-api" "^6.5.0"
+
+vue-image-crop-upload@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/vue-image-crop-upload/-/vue-image-crop-upload-3.0.3.tgz#0183cf8ebee30c08afba09a6fcb0a8fcd395e236"
+  integrity sha512-VeBsU0oI1hXeCvdpnu19DM/r3KTlI8SUXTxsHsU4MhDXR0ahRziiL9tf4FbILGx+gRVNZhGbl32yuM6TiaGNhA==
+  dependencies:
+    babel-runtime "^6.11.6"
+
+vue-json-pretty@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/vue-json-pretty/-/vue-json-pretty-2.2.4.tgz#4c82fa78aeb987460c727c3b31e45a58f58d9c95"
+  integrity sha512-JX80b3QDrspcH43C53CdtYeq/froApQGSV5y43bEMWFj2LGOxB96aH1VmvrFA21nD1WTP6nwfFMQqGXuS4jyFQ==
+
+vue-router@^4.0.0:
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
+  integrity sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==
+  dependencies:
+    "@vue/devtools-api" "^6.5.0"
+
+vue3-country-region-select@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/vue3-country-region-select/-/vue3-country-region-select-1.0.0.tgz#f2356d55bfc6892721e40e70f829949abb730cef"
+  integrity sha512-4sHzVqd2AExz8Ijo2npiHCM6X50iD8AN2rtBZGeZc6y/LG4yNLzDFImZDT3UjBaAUFNUqQ1HR5akcQ1jrIuy5g==
+  dependencies:
+    vue "^3.0.0"
+    vue-i18n "^9.0.0"
+
+vue@^3.0.0:
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-3.3.7.tgz#972a218682443a3819d121261b2bff914417f4f0"
+  integrity sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==
+  dependencies:
+    "@vue/compiler-dom" "3.3.7"
+    "@vue/compiler-sfc" "3.3.7"
+    "@vue/runtime-dom" "3.3.7"
+    "@vue/server-renderer" "3.3.7"
+    "@vue/shared" "3.3.7"
+
+vuejs3-datepicker@^1.0.19:
+  version "1.0.19"
+  resolved "https://registry.yarnpkg.com/vuejs3-datepicker/-/vuejs3-datepicker-1.0.19.tgz#33499b6ac77ad2f966c34e79a2f997390ec02d0e"
+  integrity sha512-DKy0PaT5nq319yc1sd9I66j4BeQMhvXp//IqmR3IcxePY/yRDQCFRriFS6xzuH1iCw239Zvbsf9vF77+j+gWIQ==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+    prismjs "^1.22.0"
+    vue "^3.0.0"
+
+wcwidth@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+  integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==
+  dependencies:
+    defaults "^1.0.3"
+
+webpack-merge@^5.8.0:
+  version "5.10.0"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
+  integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
+  dependencies:
+    clone-deep "^4.0.1"
+    flat "^5.0.2"
+    wildcard "^2.0.0"
+
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
+wildcard@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
+  integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
+
+wrap-ansi@^6.0.1:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+xml-name-validator@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+  integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
+y18n@^5.0.5:
+  version "5.0.8"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+  integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yaml-eslint-parser@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz#c7f5f3904f1c06ad55dc7131a731b018426b4898"
+  integrity sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==
+  dependencies:
+    eslint-visitor-keys "^1.3.0"
+    lodash "^4.17.20"
+    yaml "^1.10.0"
+
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+
+yargs-parser@^21.1.1:
+  version "21.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+  integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@^17.5.1:
+  version "17.7.2"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+  integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+  dependencies:
+    cliui "^8.0.1"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
+    require-directory "^2.1.1"
+    string-width "^4.2.3"
+    y18n "^5.0.5"
+    yargs-parser "^21.1.1"
+
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zhead@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/zhead/-/zhead-2.2.4.tgz#87cd1e2c3d2f465fa9f43b8db23f9716dfe6bed7"
+  integrity sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==
+
+zip-stream@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.1.tgz#1337fe974dbaffd2fa9a1ba09662a66932bd7135"
+  integrity sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==
+  dependencies:
+    archiver-utils "^3.0.4"
+    compress-commons "^4.1.2"
+    readable-stream "^3.6.0"

+ 1 - 1
hichina-main-front-mobile-first/quasar.config.js

@@ -127,7 +127,7 @@ module.exports = configure(function (ctx) {
       // manualStoreHydration: true,
       // manualPostHydrationTrigger: true,
 
-      prodPort: 3000, // The default port that the production server should use
+      prodPort: 9583, // The default port that the production server should use
       // (gets superseded if process.env.PORT is specified at runtime)
 
       maxAge: 1000 * 60 * 60 * 24 * 30,

+ 8 - 0
hichina-main-front-mobile-first/src-ssr/middlewares/compression.js

@@ -0,0 +1,8 @@
+import compression from 'compression'
+import { ssrMiddleware } from 'quasar/wrappers'
+
+export default ssrMiddleware(({ app }) => {
+  app.use(
+    compression({ threshold: 0 })
+  )
+})

+ 54 - 0
hichina-main-front-mobile-first/src-ssr/middlewares/render.js

@@ -0,0 +1,54 @@
+import { ssrMiddleware } from 'quasar/wrappers'
+
+// This middleware should execute as last one
+// since it captures everything and tries to
+// render the page with Vue
+
+export default ssrMiddleware(({ app, resolve, render, serve }) => {
+  // we capture any other Express route and hand it
+  // over to Vue and Vue Router to render our page
+  app.get(resolve.urlPath('*'), (req, res) => {
+    res.setHeader('Content-Type', 'text/html')
+
+    render(/* the ssrContext: */ { req, res })
+      .then(html => {
+        // now let's send the rendered html to the client
+        res.send(html)
+      })
+      .catch(err => {
+        // oops, we had an error while rendering the page
+
+        // we were told to redirect to another URL
+        if (err.url) {
+          if (err.code) {
+            res.redirect(err.code, err.url)
+          } else {
+            res.redirect(err.url)
+          }
+        } else if (err.code === 404) {
+          // hmm, Vue Router could not find the requested route
+
+          // Should reach here only if no "catch-all" route
+          // is defined in /src/routes
+          res.status(404).send('404 | Page Not Found')
+        } else if (process.env.DEV) {
+          // well, we treat any other code as error;
+          // if we're in dev mode, then we can use Quasar CLI
+          // to display a nice error page that contains the stack
+          // and other useful information
+
+          // serve.error is available on dev only
+          serve.error({ err, req, res })
+        } else {
+          // we're in production, so we should have another method
+          // to display something to the client when we encounter an error
+          // (for security reasons, it's not ok to display the same wealth
+          // of information as we do in development)
+
+          // Render Error Page on production or
+          // create a route (/src/routes) for an error page and redirect to it
+          res.status(500).send('500 | Internal Server Error')
+        }
+      })
+  })
+})

+ 17 - 0
hichina-main-front-mobile-first/src-ssr/production-export.js

@@ -0,0 +1,17 @@
+/**
+ * Start the SSR server or export your handler for serverless use
+ * or export whatever else fits your needs.
+ *
+ * https://v2.quasar.dev/quasar-cli/developing-ssr/ssr-production-export
+ *
+ * This file is used ONLY on production.
+ */
+
+import { ssrProductionExport } from 'quasar/wrappers'
+
+export default ssrProductionExport(async ({ app, port, isReady }) => {
+  await isReady()
+  return app.listen(port, () => {
+    console.log('Server listening at port ' + port)
+  })
+})

+ 10 - 0
hichina-main-front-mobile-first/src-ssr/ssr-flag.d.ts

@@ -0,0 +1,10 @@
+/* eslint-disable */
+// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
+//  REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
+import "quasar/dist/types/feature-flag";
+
+declare module "quasar/dist/types/feature-flag" {
+  interface QuasarFeatureFlags {
+    ssr: true;
+  }
+}