Эх сурвалжийг харах

Merge branch 'development' of ssh://gogs-softpar/Softpar/sfp_front_vue_diarista_prestador into development

kayo henrique 3 долоо хоног өмнө
parent
commit
757f98ce97
100 өөрчлөгдсөн 1088 нэмэгдсэн , 3227 устгасан
  1. 6 0
      .env.app.dev
  2. 7 3
      .gitignore
  3. 20 0
      package-lock.json
  4. 13 7
      package.json
  5. 63 14
      quasar.config.js
  6. 259 0
      quasar.config.js.temporary.compiled.1780666187782.mjs
  7. 3 3
      src-capacitor/android/.gitignore
  8. 24 6
      src-capacitor/android/app/build.gradle
  9. 6 0
      src-capacitor/android/app/capacitor.build.gradle
  10. 48 0
      src-capacitor/android/app/google-services.json
  11. 45 18
      src-capacitor/android/app/proguard-rules.pro
  12. 7 0
      src-capacitor/android/app/src/main/AndroidManifest.xml
  13. 26 0
      src-capacitor/android/app/src/main/java/br/com/diarista/provider/MainActivity.java
  14. 0 20
      src-capacitor/android/app/src/main/java/inf/br/softpar/skeleton/MainActivity.java
  15. BIN
      src-capacitor/android/app/src/main/res/drawable-land-hdpi/splash.png
  16. BIN
      src-capacitor/android/app/src/main/res/drawable-land-mdpi/splash.png
  17. BIN
      src-capacitor/android/app/src/main/res/drawable-land-xhdpi/splash.png
  18. BIN
      src-capacitor/android/app/src/main/res/drawable-land-xxhdpi/splash.png
  19. BIN
      src-capacitor/android/app/src/main/res/drawable-land-xxxhdpi/splash.png
  20. BIN
      src-capacitor/android/app/src/main/res/drawable-port-hdpi/splash.png
  21. BIN
      src-capacitor/android/app/src/main/res/drawable-port-mdpi/splash.png
  22. BIN
      src-capacitor/android/app/src/main/res/drawable-port-xhdpi/splash.png
  23. BIN
      src-capacitor/android/app/src/main/res/drawable-port-xxhdpi/splash.png
  24. BIN
      src-capacitor/android/app/src/main/res/drawable-port-xxxhdpi/splash.png
  25. 0 34
      src-capacitor/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  26. 0 170
      src-capacitor/android/app/src/main/res/drawable/ic_launcher_background.xml
  27. BIN
      src-capacitor/android/app/src/main/res/drawable/splash.png
  28. 3 2
      src-capacitor/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  29. 3 2
      src-capacitor/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  30. BIN
      src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  31. BIN
      src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
  32. BIN
      src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  33. BIN
      src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
  34. BIN
      src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  35. BIN
      src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  36. BIN
      src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
  37. BIN
      src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  38. BIN
      src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
  39. BIN
      src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  40. BIN
      src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  41. BIN
      src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
  42. BIN
      src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  43. BIN
      src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
  44. BIN
      src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  45. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  46. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
  47. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  48. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png
  49. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  50. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  51. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
  52. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  53. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
  54. BIN
      src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  55. 0 4
      src-capacitor/android/app/src/main/res/values/ic_launcher_background.xml
  56. 4 4
      src-capacitor/android/app/src/main/res/values/strings.xml
  57. 1 1
      src-capacitor/android/build.gradle
  58. 18 0
      src-capacitor/android/capacitor.settings.gradle
  59. 1 1
      src-capacitor/android/variables.gradle
  60. 11 3
      src-capacitor/capacitor.config.json
  61. 2 2
      src-capacitor/ios/App/App.xcodeproj/project.pbxproj
  62. BIN
      src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png
  63. BIN
      src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png
  64. BIN
      src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png
  65. 30 0
      src-capacitor/ios/App/App/GoogleService-Info.plist
  66. 2 0
      src-capacitor/ios/App/Podfile
  67. 197 102
      src-capacitor/package-lock.json
  68. 10 4
      src-capacitor/package.json
  69. 3 0
      src-capacitor/www/index.html
  70. 6 0
      src/api/chatbot.js
  71. 4 17
      src/api/providerPaymentMethod.js
  72. 7 2
      src/api/providerWithdrawal.js
  73. 22 30
      src/api/user.js
  74. 0 77
      src/components/charts/CardIconChart.vue
  75. 0 81
      src/components/charts/CardIconMiniChart.vue
  76. 0 285
      src/components/charts/custom/NPSChart.vue
  77. 0 201
      src/components/charts/custom/SpeedometerChart.vue
  78. 0 41
      src/components/charts/maps/Brasil/DadosBrasil.js
  79. 0 228
      src/components/charts/maps/Brasil/MapaBrasil.vue
  80. 0 110
      src/components/charts/maps/MapaEstado.vue
  81. 0 14
      src/components/charts/maps/Paraguai/DadosParaguai.js
  82. 0 225
      src/components/charts/maps/Paraguai/MapaParaguai.vue
  83. 0 101
      src/components/charts/mini/MiniBarChart.vue
  84. 0 116
      src/components/charts/mini/MiniLineChart.vue
  85. 0 267
      src/components/charts/normal/BarChart.vue
  86. 0 204
      src/components/charts/normal/DoughnutChart.vue
  87. 0 265
      src/components/charts/normal/LineChart.vue
  88. 0 205
      src/components/charts/normal/PieChart.vue
  89. 5 13
      src/components/dashboard/DashboardHeaderBar.vue
  90. 24 41
      src/components/dashboard/DashboardNextSchedules.vue
  91. 21 38
      src/components/dashboard/DashboardOpportunities.vue
  92. 8 19
      src/components/dashboard/DashboardPriceSuggest.vue
  93. 8 1
      src/components/dashboard/DashboardScrollAreaSchedules.vue
  94. 20 56
      src/components/dashboard/DashboardSolicitations.vue
  95. 7 10
      src/components/dashboard/DashboardSummaryInfos.vue
  96. 62 70
      src/components/dashboard/DashboardTodayServices.vue
  97. 26 24
      src/components/dashboard/NextSchedulesDetailsDialog.vue
  98. 8 15
      src/components/dashboard/ScheduleCancelDialog.vue
  99. 10 15
      src/components/dashboard/ScheduleRatingDialog.vue
  100. 38 56
      src/components/dashboard/SolicitationDetailsDialog.vue

+ 6 - 0
.env.app.dev

@@ -0,0 +1,6 @@
+API_URL=http://localhost:3000
+PASSWORD=S@ft2080.
+WEBSOCKET_API=http://localhost:4321/
+WEBSOCKET_PATH=/socket.io
+WEBSOCKET_ROOM=LARAVEL
+WEBSOCKET_API_KEY=7wArC/kl0nTbt4zBu0agw.NXLyjA96I6x1XmBcuokwPqfo3/CIxzqYw.PTthh5eqa08Uf4ubFlOqatpShoz1CRRID9pZReEFvBk3il6E9u

+ 7 - 3
.gitignore

@@ -6,6 +6,7 @@
 .env
 .env.*
 !.env.example
+!.env.app.dev
 
 npm-debug.log*
 yarn-debug.log*
@@ -21,9 +22,12 @@ Thumbs.db
 *.swp
 *.swo
 
-/src-cordova/platforms
-/src-cordova/plugins
-/src-cordova/www
+/src-capacitor/node_modules
+/src-capacitor/platforms
+/src-capacitor/plugins
+/src-capacitor/www
+/src-capacitor/www/
+/src-capacitor/www/assets
 
 /android
 /ios

+ 20 - 0
package-lock.json

@@ -9,6 +9,8 @@
       "version": "0.0.1",
       "dependencies": {
         "@bufbuild/protobuf": "^2.5.1",
+        "@capacitor/browser": "^8.0.3",
+        "@capacitor/camera": "^7.0.5",
         "@capacitor/device": "^7.0.1",
         "@capacitor/geolocation": "^8.2.0",
         "@capacitor/google-maps": "^8.0.1",
@@ -313,6 +315,24 @@
         "typescript": "5.4.5"
       }
     },
+    "node_modules/@capacitor/browser": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/@capacitor/browser/-/browser-8.0.3.tgz",
+      "integrity": "sha512-WJWPHEPbweiFoHYmVlCbZf5yrqJ2Rchx2Xvbmd+3Lf+Zkpq3nXBThThY2CF69lYEg1NINGF9BcHThIOEU1gZlQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=8.0.0"
+      }
+    },
+    "node_modules/@capacitor/camera": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-7.0.5.tgz",
+      "integrity": "sha512-+53f1xHq8on7O+ULXbgWp00ooWvn3fUkWxTP3p5zHi1nVriCxLbYnY0zTpZQqn52M96vclFKkoABbTVQ3x755w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=7.0.0"
+      }
+    },
     "node_modules/@capacitor/core": {
       "version": "8.3.4",
       "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-8.3.4.tgz",

+ 13 - 7
package.json

@@ -2,7 +2,7 @@
   "name": "diarista-prestador-app",
   "version": "0.0.1",
   "description": "Aplicativo para o prestador do Diarista",
-  "productName": "Diarista Prestador App",
+  "productName": "Diarista Profissional App",
   "author": "zntt <zanattagg@gmail.com>",
   "type": "module",
   "private": true,
@@ -10,15 +10,21 @@
     "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",
-    "dev:android": "quasar dev -m capacitor -T android",
-    "dev:ios": "quasar dev -m capacitor -T ios",
-    "build": "quasar build",
-    "build:ios": "quasar build -m capacitor -T ios --ide",
-    "build:android": "quasar build -m capacitor -T android --ide"
+    "dev": "APP_ENV=dev quasar dev",
+    "dev:staging": "APP_ENV=staging quasar dev",
+    "dev:android": "APP_ENV=dev quasar dev -m capacitor -T android",
+    "dev:ios": "APP_ENV=dev quasar dev -m capacitor -T ios",
+    "build": "APP_ENV=prod quasar build",
+    "build:dev": "APP_ENV=dev quasar build",
+    "build:staging": "APP_ENV=staging quasar build",
+    "build:prod": "APP_ENV=prod quasar build",
+    "build:ios": "APP_ENV=prod quasar build -m capacitor -T ios --ide",
+    "build:android": "APP_ENV=prod quasar build -m capacitor -T android --ide"
   },
   "dependencies": {
     "@bufbuild/protobuf": "^2.5.1",
+    "@capacitor/browser": "^8.0.3",
+    "@capacitor/camera": "^7.0.5",
     "@capacitor/device": "^7.0.1",
     "@capacitor/geolocation": "^8.2.0",
     "@capacitor/google-maps": "^8.0.1",

+ 63 - 14
quasar.config.js

@@ -4,12 +4,70 @@
 // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
 
 import { defineConfig } from "#q-app/wrappers";
+import { existsSync, readFileSync } from "node:fs";
 import { fileURLToPath } from "node:url";
-import { config as dotenvConfig } from "dotenv";
+import { resolve } from "node:path";
 
-dotenvConfig();
+const envFiles = {
+  dev:     ".env.app.dev",
+  staging: ".env.app.staging",
+  prod:    ".env.app.prod",
+};
+
+const loadAppEnv = (ctx) => {
+  const appEnv = process.env.APP_ENV || (ctx.dev ? "dev" : "prod");
+
+  const envFile = envFiles[appEnv];
+
+  if (!envFile) {
+    throw new Error(`APP_ENV invalido: "${appEnv}". Use dev, staging ou prod.`);
+  }
+
+  const fileEnv = parseEnvFile(resolve(process.cwd(), envFile));
+
+  return {
+    APP_ENV:           appEnv,
+    API_URL:           fileEnv.API_URL,
+    PASSWORD:          fileEnv.PASSWORD,
+    WEBSOCKET_API:     fileEnv.WEBSOCKET_API,
+    WEBSOCKET_PATH:    fileEnv.WEBSOCKET_PATH,
+    WEBSOCKET_ROOM:    fileEnv.WEBSOCKET_ROOM,
+    WEBSOCKET_API_KEY: fileEnv.WEBSOCKET_API_KEY,
+  };
+};
+
+const parseEnvFile = (filePath) => {
+  if (!existsSync(filePath)) {
+    return {};
+  }
+
+  return readFileSync(filePath, "utf8")
+    .split(/\r?\n/)
+    .reduce((env, line) => {
+      const trimmedLine = line.trim();
+
+      if (!trimmedLine || trimmedLine.startsWith("#")) {
+        return env;
+      }
+
+      const separatorIndex = trimmedLine.indexOf("=");
+
+      if (separatorIndex === -1) {
+        return env;
+      }
+
+      const key   = trimmedLine.slice(0, separatorIndex).trim();
+      const value = trimmedLine.slice(separatorIndex + 1).trim();
+
+      env[key] = value.replace(/^["']|["']$/g, "");
+
+      return env;
+    }, {});
+};
 
 export default defineConfig((ctx) => {
+  const appEnv = loadAppEnv(ctx);
+
   return {
     bin: {
       linuxAndroidStudio: "/snap/bin/android-studio",
@@ -61,18 +119,9 @@ export default defineConfig((ctx) => {
 
       // publicPath: '/',
       // analyze: true,
-      env: {
-        API_URL: ctx.dev ? "http://localhost:3000" : "http://localhost:3000",
-        PASSWORD: ctx.dev ? "S@ft2080." : "",
-        WEBSOCKET_API: ctx.dev
-          ? "http://localhost:4321/"
-          : "http://localhost:4321/",
-        WEBSOCKET_PATH: ctx.dev ? "/socket.io" : "/socket.io",
-        WEBSOCKET_ROOM: ctx.dev ? "LARAVEL" : "LARAVEL",
-        WEBSOCKET_API_KEY:
-          "7wArC/kl0nTbt4zBu0agw.NXLyjA96I6x1XmBcuokwPqfo3/CIxzqYw.PTthh5eqa08Uf4ubFlOqatpShoz1CRRID9pZReEFvBk3il6E9u",
-        GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
-      },
+
+      env: appEnv,
+
       // rawDefine: {}
       // ignorePublicFolder: true,
       // minify: false,

+ 259 - 0
quasar.config.js.temporary.compiled.1780666187782.mjs

@@ -0,0 +1,259 @@
+/* eslint-disable */
+/**
+ * THIS FILE IS GENERATED AUTOMATICALLY.
+ * 1. DO NOT edit this file directly as it won't do anything.
+ * 2. EDIT the original quasar.config file INSTEAD.
+ * 3. DO NOT git commit this file. It should be ignored.
+ *
+ * This file is still here because there was an error in
+ * the original quasar.config file and this allows you to
+ * investigate the Node.js stack error.
+ *
+ * After you fix the original file, this file will be
+ * deleted automatically.
+ **/
+
+
+// quasar.config.js
+import { defineConfig } from "@quasar/app-vite/wrappers";
+import { existsSync, readFileSync } from "node:fs";
+import { fileURLToPath } from "node:url";
+import { resolve } from "node:path";
+var __quasar_inject_import_meta_url__ = "file:///home/softpar/projetos/spas/sfp_front_vue_diarista_prestador/quasar.config.js";
+var envFiles = {
+  dev: ".env.app.dev",
+  staging: ".env.app.staging",
+  prod: ".env.app.prod"
+};
+var loadAppEnv = (ctx) => {
+  const appEnv = process.env.APP_ENV || (ctx.dev ? "dev" : "prod");
+  const envFile = envFiles[appEnv];
+  if (!envFile) {
+    throw new Error(`APP_ENV invalido: "${appEnv}". Use dev, staging ou prod.`);
+  }
+  const fileEnv = parseEnvFile(resolve(process.cwd(), envFile));
+  return {
+    APP_ENV: appEnv,
+    API_URL: fileEnv.API_URL,
+    PASSWORD: fileEnv.PASSWORD,
+    WEBSOCKET_API: fileEnv.WEBSOCKET_API,
+    WEBSOCKET_PATH: fileEnv.WEBSOCKET_PATH,
+    WEBSOCKET_ROOM: fileEnv.WEBSOCKET_ROOM,
+    WEBSOCKET_API_KEY: fileEnv.WEBSOCKET_API_KEY
+  };
+};
+var parseEnvFile = (filePath) => {
+  if (!existsSync(filePath)) {
+    return {};
+  }
+  return readFileSync(filePath, "utf8").split(/\r?\n/).reduce((env, line) => {
+    const trimmedLine = line.trim();
+    if (!trimmedLine || trimmedLine.startsWith("#")) {
+      return env;
+    }
+    const separatorIndex = trimmedLine.indexOf("=");
+    if (separatorIndex === -1) {
+      return env;
+    }
+    const key = trimmedLine.slice(0, separatorIndex).trim();
+    const value = trimmedLine.slice(separatorIndex + 1).trim();
+    env[key] = value.replace(/^["']|["']$/g, "");
+    return env;
+  }, {});
+};
+var quasar_config_default = defineConfig((ctx) => {
+  const appEnv = loadAppEnv(ctx);
+  return {
+    bin: {
+      linuxAndroidStudio: "/snap/bin/android-studio"
+    },
+    // 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: [
+      "axios",
+      "i18n",
+      "defaultPropsComponents",
+      "push-notifications"
+      // "socket.io",
+    ],
+    // 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-v7",
+      // '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: ["es2022", "firefox115", "chrome115", "safari14"],
+        node: "node22"
+      },
+      vueRouterMode: "history",
+      // available values: 'hash', 'history'
+      // vueRouterBase,
+      // vueDevtools,
+      // vueOptionsAPI: false,
+      // rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
+      // publicPath: '/',
+      // analyze: true,
+      env: appEnv,
+      // rawDefine: {}
+      // ignorePublicFolder: true,
+      // minify: false,
+      // polyfillModulePreload: true,
+      // distDir
+      // extendViteConf (viteConf) {},
+      // viteVuePluginOptions: {},
+      vitePlugins: [
+        [
+          "@intlify/unplugin-vue-i18n/vite",
+          {
+            include: [fileURLToPath(new URL("./src/i18n", __quasar_inject_import_meta_url__))],
+            ssr: ctx.modeName === "ssr"
+          }
+        ],
+        [
+          "vite-plugin-checker",
+          {
+            eslint: {
+              lintCommand: 'eslint -c ./eslint.config.js "./src*/**/*.{js,mjs,cjs,vue}"',
+              useFlatConfig: true
+            }
+          },
+          { server: false }
+        ]
+      ]
+    },
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
+    devServer: {
+      // https: true
+      open: false
+      // opens browser window automatically
+    },
+    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
+    framework: {
+      lang: "pt-BR",
+      config: {
+        dark: "auto",
+        notify: {
+          position: "top-right"
+        }
+      },
+      // 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: ["Dialog", "Notify", "Loading", "Cookies", "Dark"]
+    },
+    // 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',
+    //   pwaRegisterServiceWorker: 'src-pwa/register-service-worker',
+    //   pwaServiceWorker: 'src-pwa/custom-service-worker',
+    //   pwaManifestFile: 'src-pwa/manifest.json',
+    //   electronMain: 'src-electron/electron-main',
+    //   electronPreload: 'src-electron/electron-preload'
+    //   bexManifestFile: 'src-bex/manifest.json
+    // },
+    // https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
+    ssr: {
+      prodPort: 3e3,
+      // 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
+      ],
+      // extendPackageJson (json) {},
+      // extendSSRWebserverConf (esbuildConf) {},
+      // manualStoreSerialization: true,
+      // manualStoreSsrContextInjection: true,
+      // manualStoreHydration: true,
+      // manualPostHydrationTrigger: true,
+      pwa: false
+      // pwaOfflineHtmlFilename: 'offline.html', // do NOT use index.html as name!
+      // will mess up SSR
+      // pwaExtendGenerateSWOptions (cfg) {},
+      // pwaExtendInjectManifestOptions (cfg) {}
+    },
+    // https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
+    pwa: {
+      workboxMode: "GenerateSW"
+      // 'GenerateSW' or 'InjectManifest'
+      // swFilename: 'sw.js',
+      // manifestFilename: 'manifest.json'
+      // extendManifestJson (json) {},
+      // useCredentialsForManifestTag: true,
+      // injectPwaMetaTags: false,
+      // extendPWACustomSWConf (esbuildConf) {},
+      // extendGenerateSWOptions (cfg) {},
+      // extendInjectManifestOptions (cfg) {}
+    },
+    // 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) {},
+      // extendPackageJson (json) {},
+      // Electron preload scripts (if any) from /src-electron, WITHOUT file extension
+      preloadScripts: ["electron-preload"],
+      // 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: "quasar-skeleton"
+      }
+    },
+    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
+    bex: {
+      // extendBexScriptsConf (esbuildConf) {},
+      // extendBexManifestJson (json) {},
+      contentScripts: ["my-content-script"]
+    }
+  };
+});
+export {
+  quasar_config_default as default
+};

+ 3 - 3
src-capacitor/android/.gitignore

@@ -53,9 +53,9 @@ captures/
 .idea/navEditor.xml
 
 # Keystore files
-# Uncomment the following lines if you do not want to check your keystore files in.
-#*.jks
-#*.keystore
+*.jks
+*.keystore
+keystore.properties
 
 # External native build folder generated in Android Studio 2.2 and later
 .externalNativeBuild

+ 24 - 6
src-capacitor/android/app/build.gradle

@@ -1,14 +1,20 @@
 apply plugin: 'com.android.application'
 
+def keystorePropertiesFile = rootProject.file("keystore.properties")
+def keystoreProperties = new Properties()
+if (keystorePropertiesFile.exists()) {
+    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+}
+
 android {
-    namespace "inf.br.softpar.skeleton"
+    namespace "br.com.diarista.provider"
     compileSdk rootProject.ext.compileSdkVersion
     defaultConfig {
-        applicationId "inf.br.softpar.skeleton"
+        applicationId "br.com.diarista.provider"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 1
-        versionName "1.0"
+        versionCode 3
+        versionName "1.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -16,10 +22,22 @@ android {
             ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
         }
     }
+    signingConfigs {
+        release {
+            if (keystorePropertiesFile.exists()) {
+                storeFile file(keystoreProperties['storeFile'])
+                storePassword keystoreProperties['storePassword']
+                keyAlias keystoreProperties['keyAlias']
+                keyPassword keystoreProperties['keyPassword']
+            }
+        }
+    }
     buildTypes {
         release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.release
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
     }
 }

+ 6 - 0
src-capacitor/android/app/capacitor.build.gradle

@@ -10,9 +10,15 @@ android {
 apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
 dependencies {
     implementation project(':capacitor-app')
+    implementation project(':capacitor-browser')
+    implementation project(':capacitor-camera')
     implementation project(':capacitor-device')
+    implementation project(':capacitor-geolocation')
+    implementation project(':capacitor-google-maps')
     implementation project(':capacitor-keyboard')
     implementation project(':capacitor-preferences')
+    implementation project(':capacitor-push-notifications')
+    implementation project(':capacitor-splash-screen')
     implementation project(':capacitor-status-bar')
 
 }

+ 48 - 0
src-capacitor/android/app/google-services.json

@@ -0,0 +1,48 @@
+{
+  "project_info": {
+    "project_number": "616416880897",
+    "project_id": "diariaappsfp",
+    "storage_bucket": "diariaappsfp.firebasestorage.app"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:616416880897:android:dcd2c6113f59bf73185300",
+        "android_client_info": {
+          "package_name": "br.com.diarista.client"
+        }
+      },
+      "oauth_client": [],
+      "api_key": [
+        {
+          "current_key": "AIzaSyDOTWcQQe5iSzOM7ezrQoPBSpj8146uSIo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": []
+        }
+      }
+    },
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:616416880897:android:37c1d70a33d27018185300",
+        "android_client_info": {
+          "package_name": "br.com.diarista.provider"
+        }
+      },
+      "oauth_client": [],
+      "api_key": [
+        {
+          "current_key": "AIzaSyDOTWcQQe5iSzOM7ezrQoPBSpj8146uSIo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": []
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 45 - 18
src-capacitor/android/app/proguard-rules.pro

@@ -1,21 +1,48 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
+# Capacitor core
+-keep class com.getcapacitor.** { *; }
+-keep @com.getcapacitor.annotation.CapacitorPlugin class * { *; }
+-keep @com.getcapacitor.annotation.Permission class * { *; }
+-keepclassmembers class * extends com.getcapacitor.Plugin {
+    @com.getcapacitor.annotation.CapacitorPlugin <methods>;
+    @com.getcapacitor.PluginMethod <methods>;
+}
 
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
+# JavaScript Interface (WebView <-> Java bridge)
+-keepclassmembers class * {
+    @android.webkit.JavascriptInterface <methods>;
+}
 
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
+# WebView
+-keepclassmembers class * extends android.webkit.WebViewClient {
+    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
+    public boolean *(android.webkit.WebView, java.lang.String);
+    public void *(android.webkit.WebView, java.lang.String);
+}
 
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
+# Firebase
+-keep class com.google.firebase.** { *; }
+-keep class com.google.android.gms.** { *; }
+-dontwarn com.google.firebase.**
+-dontwarn com.google.android.gms.**
+
+# OkHttp (Capacitor HTTP plugin)
+-dontwarn okhttp3.**
+-dontwarn okio.**
+-keep class okhttp3.** { *; }
+-keep interface okhttp3.** { *; }
+
+# Kotlin
+-keep class kotlin.** { *; }
+-dontwarn kotlin.**
+-keepclassmembers class **$WhenMappings {
+    <fields>;
+}
+
+# Preserve stack traces in crash reports
+-keepattributes SourceFile,LineNumberTable
+-renamesourcefileattribute SourceFile
+
+# Annotations
+-keepattributes *Annotation*
+-keepattributes Signature
+-keepattributes Exceptions

+ 7 - 0
src-capacitor/android/app/src/main/AndroidManifest.xml

@@ -9,6 +9,10 @@
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
 
+        <meta-data
+            android:name="com.google.android.geo.API_KEY"
+            android:value="AIzaSyDrVs7amXuXKEu8sYMr-dl8VjNZbgS2frk" />
+
         <activity
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
             android:name=".MainActivity"
@@ -39,4 +43,7 @@
     <!-- Permissions -->
 
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera" android:required="false" />
+    <uses-feature android:name="android.hardware.camera.front" android:required="false" />
 </manifest>

+ 26 - 0
src-capacitor/android/app/src/main/java/br/com/diarista/provider/MainActivity.java

@@ -0,0 +1,26 @@
+package br.com.diarista.provider;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+import com.getcapacitor.BridgeActivity;
+
+public class MainActivity extends BridgeActivity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // Necessário para @capacitor/google-maps: o mapa nativo renderiza atrás do
+        // WebView, então o WebView precisa ser transparente onde o mapa é exibido.
+        // (O background do body cobre isso nas telas normais — só o LocationMapDialog
+        //  torna a cadeia html/body/#q-app transparente para revelar o mapa.)
+        getBridge().getWebView().setBackgroundColor(Color.TRANSPARENT);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        WebView webview = getBridge().getWebView();
+        webview.setOverScrollMode(WebView.OVER_SCROLL_NEVER);
+    }
+}

+ 0 - 20
src-capacitor/android/app/src/main/java/inf/br/softpar/skeleton/MainActivity.java

@@ -1,20 +0,0 @@
-package inf.br.softpar.skeleton;
-
-import android.os.Bundle;
-import android.webkit.WebView;
-
-import com.getcapacitor.BridgeActivity;
-
-public class MainActivity extends BridgeActivity {
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        WebView webview = getBridge().getWebView();
-        webview.setOverScrollMode(WebView.OVER_SCROLL_NEVER);
-    }
-}

BIN
src-capacitor/android/app/src/main/res/drawable-land-hdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-land-mdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-land-xhdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-land-xxhdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-land-xxxhdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-port-hdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-port-mdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-port-xhdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-port-xxhdpi/splash.png


BIN
src-capacitor/android/app/src/main/res/drawable-port-xxxhdpi/splash.png


+ 0 - 34
src-capacitor/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -1,34 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportHeight="108"
-    android:viewportWidth="108">
-    <path
-        android:fillType="evenOdd"
-        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
-        android:strokeColor="#00000000"
-        android:strokeWidth="1">
-        <aapt:attr name="android:fillColor">
-            <gradient
-                android:endX="78.5885"
-                android:endY="90.9159"
-                android:startX="48.7653"
-                android:startY="61.0927"
-                android:type="linear">
-                <item
-                    android:color="#44000000"
-                    android:offset="0.0" />
-                <item
-                    android:color="#00000000"
-                    android:offset="1.0" />
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path
-        android:fillColor="#FFFFFF"
-        android:fillType="nonZero"
-        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
-        android:strokeColor="#00000000"
-        android:strokeWidth="1" />
-</vector>

+ 0 - 170
src-capacitor/android/app/src/main/res/drawable/ic_launcher_background.xml

@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportHeight="108"
-    android:viewportWidth="108">
-    <path
-        android:fillColor="#26A69A"
-        android:pathData="M0,0h108v108h-108z" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M9,0L9,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,0L19,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,0L29,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,0L39,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,0L49,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,0L59,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,0L69,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,0L79,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M89,0L89,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M99,0L99,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,9L108,9"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,19L108,19"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,29L108,29"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,39L108,39"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,49L108,49"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,59L108,59"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,69L108,69"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,79L108,79"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,89L108,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,99L108,99"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,29L89,29"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,39L89,39"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,49L89,49"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,59L89,59"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,69L89,69"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,79L89,79"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,19L29,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,19L39,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,19L49,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,19L59,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,19L69,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,19L79,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-</vector>

BIN
src-capacitor/android/app/src/main/res/drawable/splash.png


+ 3 - 2
src-capacitor/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/ic_launcher_background"/>
+    <background android:drawable="@mipmap/ic_launcher_background"/>
     <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
-</adaptive-icon>
+    <monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
+</adaptive-icon>

+ 3 - 2
src-capacitor/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/ic_launcher_background"/>
+    <background android:drawable="@mipmap/ic_launcher_background"/>
     <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
-</adaptive-icon>
+    <monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
+</adaptive-icon>

BIN
src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png


BIN
src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png


BIN
src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png


BIN
src-capacitor/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png


BIN
src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png


BIN
src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png


BIN
src-capacitor/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png


BIN
src-capacitor/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 0 - 4
src-capacitor/android/app/src/main/res/values/ic_launcher_background.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="ic_launcher_background">#FFFFFF</color>
-</resources>

+ 4 - 4
src-capacitor/android/app/src/main/res/values/strings.xml

@@ -1,7 +1,7 @@
 <?xml version='1.0' encoding='utf-8'?>
 <resources>
-    <string name="app_name">Quasar App</string>
-    <string name="title_activity_main">Quasar App</string>
-    <string name="package_name">inf.br.softpar.skeleton</string>
-    <string name="custom_url_scheme">inf.br.softpar.skeleton</string>
+    <string name="app_name">Diária Profissionais</string>
+    <string name="title_activity_main">Diária Profissionais</string>
+    <string name="package_name">br.com.diarista.provider</string>
+    <string name="custom_url_scheme">br.com.diarista.provider</string>
 </resources>

+ 1 - 1
src-capacitor/android/build.gradle

@@ -7,7 +7,7 @@ buildscript {
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:8.7.2'
+        classpath 'com.android.tools.build:gradle:8.9.1'
         classpath 'com.google.gms:google-services:4.4.2'
 
         // NOTE: Do not place your application dependencies here; they belong

+ 18 - 0
src-capacitor/android/capacitor.settings.gradle

@@ -5,14 +5,32 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/
 include ':capacitor-app'
 project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
 
+include ':capacitor-browser'
+project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/browser/android')
+
+include ':capacitor-camera'
+project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
+
 include ':capacitor-device'
 project(':capacitor-device').projectDir = new File('../node_modules/@capacitor/device/android')
 
+include ':capacitor-geolocation'
+project(':capacitor-geolocation').projectDir = new File('../node_modules/@capacitor/geolocation/android')
+
+include ':capacitor-google-maps'
+project(':capacitor-google-maps').projectDir = new File('../node_modules/@capacitor/google-maps/android')
+
 include ':capacitor-keyboard'
 project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
 
 include ':capacitor-preferences'
 project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
 
+include ':capacitor-push-notifications'
+project(':capacitor-push-notifications').projectDir = new File('../node_modules/@capacitor/push-notifications/android')
+
+include ':capacitor-splash-screen'
+project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
+
 include ':capacitor-status-bar'
 project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')

+ 1 - 1
src-capacitor/android/variables.gradle

@@ -1,6 +1,6 @@
 ext {
     minSdkVersion = 23
-    compileSdkVersion = 35
+    compileSdkVersion = 36
     targetSdkVersion = 35
     androidxActivityVersion = '1.9.2'
     androidxAppCompatVersion = '1.7.0'

+ 11 - 3
src-capacitor/capacitor.config.json

@@ -1,6 +1,6 @@
 {
-  "appId": "inf.br.softpar.skeleton",
-  "appName": "Quasar App",
+  "appId": "br.com.diarista.provider",
+  "appName": "Diarista Profissional",
   "webDir": "www",
   "plugins": {
     "Keyboard": {
@@ -10,11 +10,19 @@
     "StatusBar": {
       "overlaysWebView": true
     },
+    "SplashScreen": {
+      "launchAutoHide": false,
+      "backgroundColor": "#8B5CF6",
+      "androidScaleType": "CENTER_CROP",
+      "showSpinner": false,
+      "splashFullScreen": true,
+      "splashImmersive": true
+    },
     "CapacitorCookies": {
       "enabled": true
     },
     "CapacitorHttp": {
-      "enabled": true
+      "enabled": false
     }
   }
 }

+ 2 - 2
src-capacitor/ios/App/App.xcodeproj/project.pbxproj

@@ -354,7 +354,7 @@
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				MARKETING_VERSION = 1.0;
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
-				PRODUCT_BUNDLE_IDENTIFIER = inf.br.softpar.skeleton;
+				PRODUCT_BUNDLE_IDENTIFIER = br.com.diarista.provider;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 				SWIFT_VERSION = 5.0;
@@ -373,7 +373,7 @@
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				MARKETING_VERSION = 1.0;
-				PRODUCT_BUNDLE_IDENTIFIER = inf.br.softpar.skeleton;
+				PRODUCT_BUNDLE_IDENTIFIER = br.com.diarista.provider;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
 				SWIFT_VERSION = 5.0;

BIN
src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png


BIN
src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png


BIN
src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png


+ 30 - 0
src-capacitor/ios/App/App/GoogleService-Info.plist

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>API_KEY</key>
+	<string>AIzaSyC0jyxunkfvw9i-jXclS24EEsdhX9UI4hM</string>
+	<key>GCM_SENDER_ID</key>
+	<string>616416880897</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>br.com.diarista.provider</string>
+	<key>PROJECT_ID</key>
+	<string>diariaappsfp</string>
+	<key>STORAGE_BUCKET</key>
+	<string>diariaappsfp.firebasestorage.app</string>
+	<key>IS_ADS_ENABLED</key>
+	<false></false>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false></false>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true></true>
+	<key>IS_GCM_ENABLED</key>
+	<true></true>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true></true>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:616416880897:ios:2cec8e44798687a1185300</string>
+</dict>
+</plist>

+ 2 - 0
src-capacitor/ios/App/Podfile

@@ -12,9 +12,11 @@ def capacitor_pods
   pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
   pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
   pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
+  pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
   pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
   pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
   pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
+  pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
 end
 

+ 197 - 102
src-capacitor/package-lock.json

@@ -1,21 +1,27 @@
 {
-  "name": "quasar-skeleton",
+  "name": "diarista-prestador-app",
   "version": "0.0.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "quasar-skeleton",
+      "name": "diarista-prestador-app",
       "version": "0.0.1",
       "dependencies": {
         "@capacitor/android": "^7.2.0",
         "@capacitor/app": "^7.0.0",
-        "@capacitor/cli": "^7.0.0",
+        "@capacitor/browser": "^8.0.3",
+        "@capacitor/camera": "^7.0.5",
+        "@capacitor/cli": "^8.3.3",
         "@capacitor/core": "^7.0.0",
         "@capacitor/device": "^7.0.1",
+        "@capacitor/geolocation": "^8.2.0",
+        "@capacitor/google-maps": "^8.0.1",
         "@capacitor/ios": "^7.2.0",
         "@capacitor/keyboard": "^7.0.1",
         "@capacitor/preferences": "^7.0.1",
+        "@capacitor/push-notifications": "^8.1.1",
+        "@capacitor/splash-screen": "^7.0.5",
         "@capacitor/status-bar": "^7.0.1"
       }
     },
@@ -36,10 +42,28 @@
         "@capacitor/core": ">=7.0.0"
       }
     },
+    "node_modules/@capacitor/browser": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/@capacitor/browser/-/browser-8.0.3.tgz",
+      "integrity": "sha512-WJWPHEPbweiFoHYmVlCbZf5yrqJ2Rchx2Xvbmd+3Lf+Zkpq3nXBThThY2CF69lYEg1NINGF9BcHThIOEU1gZlQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=8.0.0"
+      }
+    },
+    "node_modules/@capacitor/camera": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-7.0.5.tgz",
+      "integrity": "sha512-+53f1xHq8on7O+ULXbgWp00ooWvn3fUkWxTP3p5zHi1nVriCxLbYnY0zTpZQqn52M96vclFKkoABbTVQ3x755w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=7.0.0"
+      }
+    },
     "node_modules/@capacitor/cli": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-7.2.0.tgz",
-      "integrity": "sha512-RNW9vtYYYSDmOdguYBSW0VpRnG/d6lGydlc9DLrJ7qbSPxFrotTz9IjkM48O+SruUma61DyuSqJttdbay2xSxg==",
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-8.4.1.tgz",
+      "integrity": "sha512-t7F2s7fFHCq113xgrggrmK6ctV0/8E5YfLNVLfPHp4GCTDO+tly9fZvWPf2/sOI8lMm18dLT43qbXLRTz/OZgw==",
       "license": "MIT",
       "dependencies": {
         "@ionic/cli-framework-output": "^2.2.8",
@@ -50,13 +74,13 @@
         "env-paths": "^2.2.0",
         "fs-extra": "^11.2.0",
         "kleur": "^4.1.5",
-        "native-run": "^2.0.1",
+        "native-run": "^2.0.3",
         "open": "^8.4.0",
         "plist": "^3.1.0",
         "prompts": "^2.4.2",
         "rimraf": "^6.0.1",
         "semver": "^7.6.3",
-        "tar": "^6.1.11",
+        "tar": "^7.5.3",
         "tslib": "^2.8.1",
         "xml2js": "^0.6.2"
       },
@@ -65,7 +89,7 @@
         "capacitor": "bin/capacitor"
       },
       "engines": {
-        "node": ">=20.0.0"
+        "node": ">=22.0.0"
       }
     },
     "node_modules/@capacitor/core": {
@@ -85,6 +109,32 @@
         "@capacitor/core": ">=7.0.0"
       }
     },
+    "node_modules/@capacitor/geolocation": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/@capacitor/geolocation/-/geolocation-8.2.0.tgz",
+      "integrity": "sha512-N29QcoIPmme0xSxRkm7+3hjoHp6mBAOarxecvtCCZKyOBeKiJsFUq981cezg2XWBa6fhCXJMCCjQPngKK/dIag==",
+      "license": "MIT",
+      "dependencies": {
+        "@capacitor/synapse": "^1.0.4"
+      },
+      "peerDependencies": {
+        "@capacitor/core": ">=8.0.0"
+      }
+    },
+    "node_modules/@capacitor/google-maps": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/@capacitor/google-maps/-/google-maps-8.0.1.tgz",
+      "integrity": "sha512-XSOyanbtOeO5KrSfoJOpbcSW4EXvfdxp+SUy6umdIGFeR2WJKNnPC6isXn+zjJgJH8kblT2X4fF0dxhZV8wLBg==",
+      "license": "MIT",
+      "dependencies": {
+        "@googlemaps/js-api-loader": "^2.0.2",
+        "@googlemaps/markerclusterer": "^2.6.2",
+        "@types/google.maps": "^3.58.1"
+      },
+      "peerDependencies": {
+        "@capacitor/core": ">=8.0.0"
+      }
+    },
     "node_modules/@capacitor/ios": {
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-7.2.0.tgz",
@@ -111,6 +161,24 @@
         "@capacitor/core": ">=7.0.0"
       }
     },
+    "node_modules/@capacitor/push-notifications": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-8.1.1.tgz",
+      "integrity": "sha512-WqzjPKIbYbARMN+GC0XMAJcxJpUUzqgzS/Ny8RODLrro38pQhm3GXYwX2Mwd+LZlLY39rGImkCkrKyQSNfuikA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=8.0.0"
+      }
+    },
+    "node_modules/@capacitor/splash-screen": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-7.0.5.tgz",
+      "integrity": "sha512-bPG2SamFL7VT5I3XsgsGgJhkiyxq3OXCOVKEDupSieVdtEiG7j2vLbSD0xL+91zteF/qLuxsjbugGy5LD8mS2A==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@capacitor/core": ">=7.0.0"
+      }
+    },
     "node_modules/@capacitor/status-bar": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.1.tgz",
@@ -120,6 +188,32 @@
         "@capacitor/core": ">=7.0.0"
       }
     },
+    "node_modules/@capacitor/synapse": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@capacitor/synapse/-/synapse-1.0.4.tgz",
+      "integrity": "sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==",
+      "license": "ISC"
+    },
+    "node_modules/@googlemaps/js-api-loader": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-2.1.1.tgz",
+      "integrity": "sha512-yUpAwksbHrlZIWD49JmveNSfBG4oAK0AwMknfSaPMnP5N7UT8oFRVCqwjGb1XQovi//7KLbPQKZpbofiLGzpDw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/google.maps": "^3.53.1"
+      }
+    },
+    "node_modules/@googlemaps/markerclusterer": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.6.2.tgz",
+      "integrity": "sha512-U6uVhq8iWhiIckA89sgRu8OK35mjd6/3CuoZKWakKEf0QmRRWpatlsPb3kqXkoWSmbcZkopRiI4dnW6DQSd7bQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/supercluster": "^7.1.3",
+        "fast-equals": "^5.2.2",
+        "supercluster": "^8.0.1"
+      }
+    },
     "node_modules/@ionic/cli-framework-output": {
       "version": "2.2.8",
       "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz",
@@ -355,6 +449,18 @@
         "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
       }
     },
+    "node_modules/@isaacs/fs-minipass": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+      "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+      "license": "ISC",
+      "dependencies": {
+        "minipass": "^7.0.4"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
     "node_modules/@types/fs-extra": {
       "version": "8.1.5",
       "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz",
@@ -364,6 +470,18 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/geojson": {
+      "version": "7946.0.16",
+      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+      "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/google.maps": {
+      "version": "3.65.2",
+      "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.65.2.tgz",
+      "integrity": "sha512-e52bmOhGCQSNabFpL48iQlwJybq6rfns8NUVJ20MR7CdPlHQ2RmSCnPbJfrUYJfogrE4OiHQTZ4LXpop+eer1w==",
+      "license": "MIT"
+    },
     "node_modules/@types/node": {
       "version": "22.14.1",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
@@ -379,13 +497,22 @@
       "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==",
       "license": "MIT"
     },
+    "node_modules/@types/supercluster": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz",
+      "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/geojson": "*"
+      }
+    },
     "node_modules/@xmldom/xmldom": {
-      "version": "0.8.10",
-      "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
-      "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
+      "version": "0.9.10",
+      "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz",
+      "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==",
       "license": "MIT",
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=14.6"
       }
     },
     "node_modules/ansi-regex": {
@@ -496,12 +623,12 @@
       }
     },
     "node_modules/chownr": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
-      "license": "ISC",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+      "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+      "license": "BlueOak-1.0.0",
       "engines": {
-        "node": ">=10"
+        "node": ">=18"
       }
     },
     "node_modules/color-convert": {
@@ -604,6 +731,15 @@
         "node": ">=6"
       }
     },
+    "node_modules/fast-equals": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz",
+      "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/fd-slicer": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -655,30 +791,6 @@
         "node": ">=14.14"
       }
     },
-    "node_modules/fs-minipass": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-      "license": "ISC",
-      "dependencies": {
-        "minipass": "^3.0.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/fs-minipass/node_modules/minipass": {
-      "version": "3.3.6",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/glob": {
       "version": "11.0.1",
       "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
@@ -792,6 +904,12 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/kdbush": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz",
+      "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==",
+      "license": "ISC"
+    },
     "node_modules/kleur": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -835,40 +953,15 @@
       }
     },
     "node_modules/minizlib": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+      "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
       "license": "MIT",
       "dependencies": {
-        "minipass": "^3.0.0",
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/minizlib/node_modules/minipass": {
-      "version": "3.3.6",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
-      "license": "ISC",
-      "dependencies": {
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
-      "license": "MIT",
-      "bin": {
-        "mkdirp": "bin/cmd.js"
+        "minipass": "^7.1.2"
       },
       "engines": {
-        "node": ">=10"
+        "node": ">= 18"
       }
     },
     "node_modules/ms": {
@@ -878,9 +971,9 @@
       "license": "MIT"
     },
     "node_modules/native-run": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.1.tgz",
-      "integrity": "sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.3.tgz",
+      "integrity": "sha512-U1PllBuzW5d1gfan+88L+Hky2eZx+9gv3Pf6rNBxKbORxi7boHzqiA6QFGSnqMem4j0A9tZ08NMIs5+0m/VS1Q==",
       "license": "MIT",
       "dependencies": {
         "@ionic/utils-fs": "^3.1.7",
@@ -957,12 +1050,12 @@
       "license": "MIT"
     },
     "node_modules/plist": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
-      "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz",
+      "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==",
       "license": "MIT",
       "dependencies": {
-        "@xmldom/xmldom": "^0.8.8",
+        "@xmldom/xmldom": "^0.9.10",
         "base64-js": "^1.5.1",
         "xmlbuilder": "^15.1.1"
       },
@@ -1185,30 +1278,29 @@
         "node": ">=8"
       }
     },
-    "node_modules/tar": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
-      "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+    "node_modules/supercluster": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
+      "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
       "license": "ISC",
       "dependencies": {
-        "chownr": "^2.0.0",
-        "fs-minipass": "^2.0.0",
-        "minipass": "^5.0.0",
-        "minizlib": "^2.1.1",
-        "mkdirp": "^1.0.3",
-        "yallist": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=10"
+        "kdbush": "^4.0.2"
       }
     },
-    "node_modules/tar/node_modules/minipass": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
-      "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
-      "license": "ISC",
+    "node_modules/tar": {
+      "version": "7.5.19",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
+      "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/fs-minipass": "^4.0.0",
+        "chownr": "^3.0.0",
+        "minipass": "^7.1.2",
+        "minizlib": "^3.1.0",
+        "yallist": "^5.0.0"
+      },
       "engines": {
-        "node": ">=8"
+        "node": ">=18"
       }
     },
     "node_modules/through2": {
@@ -1347,10 +1439,13 @@
       }
     },
     "node_modules/yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "license": "ISC"
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+      "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": ">=18"
+      }
     },
     "node_modules/yauzl": {
       "version": "2.10.0",

+ 10 - 4
src-capacitor/package.json

@@ -1,18 +1,24 @@
 {
-  "name": "quasar-skeleton",
+  "name": "diarista-prestador-app",
   "version": "0.0.1",
-  "description": "A skeleton for future projects",
-  "author": "Denis <denis.gnl@gmail.com>",
+  "description": "Aplicativo para o prestador do Diarista",
+  "author": "zntt <zanattagg@gmail.com>",
   "private": true,
   "dependencies": {
     "@capacitor/android": "^7.2.0",
     "@capacitor/app": "^7.0.0",
-    "@capacitor/cli": "^7.0.0",
+    "@capacitor/browser": "^8.0.3",
+    "@capacitor/camera": "^7.0.5",
+    "@capacitor/cli": "^8.3.3",
     "@capacitor/core": "^7.0.0",
     "@capacitor/device": "^7.0.1",
+    "@capacitor/geolocation": "^8.2.0",
+    "@capacitor/google-maps": "^8.0.1",
     "@capacitor/ios": "^7.2.0",
     "@capacitor/keyboard": "^7.0.1",
     "@capacitor/preferences": "^7.0.1",
+    "@capacitor/push-notifications": "^8.1.1",
+    "@capacitor/splash-screen": "^7.0.5",
     "@capacitor/status-bar": "^7.0.1"
   }
 }

+ 3 - 0
src-capacitor/www/index.html

@@ -0,0 +1,3 @@
+<!DOCTYPE html><html><head><title>Diarista Profissional App</title><meta charset=utf-8><meta name=description content="Aplicativo para o prestador do Diarista"><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,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>  <script type="module" crossorigin src="./assets/index-BFdxlQrK.js"></script>
+  <link rel="stylesheet" crossorigin href="./assets/index-DNrVWvRO.css">
+</head><body><div id=q-app></div></body></html>

+ 6 - 0
src/api/chatbot.js

@@ -0,0 +1,6 @@
+import api from 'src/api';
+
+export const sendChatMessage = async ({ message, history = [] }) => {
+  const { data } = await api.post('/chatbot/message', { message, history });
+  return data.payload.reply;
+};

+ 4 - 17
src/api/providerPaymentMethod.js

@@ -1,21 +1,8 @@
 import api from "src/api";
 
-export const getProviderPaymentMethods = async (providerId) => {
-  const { data } = await api.get(`/provider/payment-methods/${providerId}`);
-  return data.payload;
-};
-
-export const createProviderPaymentMethod = async (info) => {
-  const { data } = await api.post("/provider/payment-method", info);
-  return data.payload;
-};
-
-export const updateProviderPaymentMethod = async (id, info) => {
-  const { data } = await api.put(`/provider/payment-method/${id}`, info);
-  return data.payload;
-};
-
-export const deleteProviderPaymentMethod = async (id) => {
-  const { data } = await api.delete(`/provider/payment-method/${id}`);
+export const updateProviderBankAccount = async (providerId, bankAccount) => {
+  const { data } = await api.patch(`/provider/${providerId}/bank-account`, {
+    recipient_default_bank_account: bankAccount,
+  });
   return data.payload;
 };

+ 7 - 2
src/api/providerWithdrawal.js

@@ -5,12 +5,17 @@ export const getBalance = async () => {
   return data.payload
 }
 
+export const getWithdrawalFees = async () => {
+  const { data } = await api.get('/provider/withdrawals/fees')
+  return data.payload
+}
+
 export const getWithdrawals = async () => {
   const { data } = await api.get('/provider/withdrawals')
   return data.payload
 }
 
-export const requestWithdrawal = async () => {
-  const { data } = await api.post('/provider/withdrawals')
+export const requestWithdrawal = async (payload = {}) => {
+  const { data } = await api.post('/provider/withdrawals', payload)
   return data.payload
 }

+ 22 - 30
src/api/user.js

@@ -33,45 +33,37 @@ export const userTypes = async () => {
 export const sendCode = async (email, phone) => {
   const data = await api.post("/provider-send-code", { email, phone });
   return data;
-};
+}
 
 export const validateCode = async (email, phone, code, isLogin = false) => {
-  const data = await api.post("/validate-code-provider", {
-    email,
-    phone,
-    code,
-    isLogin,
-  });
+  const data = await api.post("/validate-code-provider", { email, phone, code, isLogin });
   return data;
 };
 
-const removeEmptyRegistrationFields = (data) => {
-  if (Array.isArray(data)) {
-    return data.map(removeEmptyRegistrationFields);
-  }
+// const removeEmptyRegistrationFields = (data) => {
+//   if (Array.isArray(data)) {
+//     return data.map(removeEmptyRegistrationFields);
+//   }
 
-  if (!data || typeof data !== "object") {
-    return data;
-  }
+//   if (!data || typeof data !== "object") {
+//     return data;
+//   }
 
-  return Object.entries(data).reduce((payload, [key, value]) => {
-    if (
-      value === undefined ||
-      key === "no_complement" ||
-      key.endsWith("_file")
-    ) {
-      return payload;
-    }
+//   return Object.entries(data).reduce((payload, [key, value]) => {
+//     if (
+//       value === undefined ||
+//       key === "no_complement" ||
+//       key.endsWith("_file")
+//     ) {
+//       return payload;
+//     }
 
-    payload[key] = removeEmptyRegistrationFields(value);
-    return payload;
-  }, {});
-};
+//     payload[key] = removeEmptyRegistrationFields(value);
+//     return payload;
+//   }, {});
+// };
 
 export const createUserAndProvider = async (data) => {
-  const response = await api.post(
-    "/register-provider",
-    removeEmptyRegistrationFields(data),
-  );
+  const response = await api.post("/register-provider", data);
   return response;
 };

+ 0 - 77
src/components/charts/CardIconChart.vue

@@ -1,77 +0,0 @@
-<template>
-  <q-card
-    flat
-    class="full-height q-pa-lg"
-    :style="{
-      minHeight: $q.screen.lt.sm ? '400px' : '600px',
-      minWidth: $q.screen.lt.sm ? '200px' : '300px',
-    }"
-  >
-    <div class="column no-wrap full-width">
-      <div class="flex justify-between items-center no-wrap">
-        <span class="text-h5">{{ title }}</span>
-        <div class="flex no-wrap flex-center">
-          <div class="round background">
-            <q-icon class="q-pa-sm" :name="icon" size="24px" :color="color" />
-          </div>
-          <!-- <q-icon
-            v-if="downloadImage !== null"
-            name="mdi-dots-vertical"
-            size="sm"
-            style="width: 12px;margin-right: -12px"
-            @click="downloadImage"
-          /> -->
-        </div>
-      </div>
-    </div>
-    <div class="flex flex-grow flex-center" style="height: calc(100% - 30px)">
-      <template v-if="hasChartSlot">
-        <slot name="chart"></slot>
-      </template>
-      <template v-else>
-        <div v-if="!loading" class="q-my-md row justify-center full-width">
-          <div class="q-pa-md body2">
-            {{ $t("http.errors.no_records_found") }}
-          </div>
-        </div>
-      </template>
-    </div>
-  </q-card>
-</template>
-
-<script setup>
-import { useSlots } from "vue";
-
-const hasChartSlot = useSlots("chart");
-
-const { color, title, icon } = defineProps({
-  color: {
-    type: String,
-    default: "primary",
-  },
-  title: {
-    type: String,
-    default: "Usuários",
-  },
-  icon: {
-    type: String,
-    default: "mdi-account",
-  },
-});
-</script>
-<style lang="scss" scoped>
-@use "sass:map";
-@use "src/css/quasar.variables.scss";
-
-body.body--light {
-  .background {
-    background: rgba(map.get($colors, "primary"), 0.2) !important;
-  }
-}
-
-body.body--dark {
-  .background {
-    background: rgba(map.get($colors-dark, "primary"), 0.2) !important;
-  }
-}
-</style>

+ 0 - 81
src/components/charts/CardIconMiniChart.vue

@@ -1,81 +0,0 @@
-<template>
-  <q-card flat class="q-pa-lg" style="max-height: 184px; min-width: 305px;">
-    <div class="column no-wrap full-width">
-      <div class="flex justify-between items-center no-wrap">
-        <span class="text-h5">{{ title }}</span>
-        <div class="round background">
-          <q-icon class="q-pa-sm" :name="icon" size="24px" :color="color" />
-        </div>
-      </div>
-      <div class="flex no-wrap full-width justify-between q-pa-sm">
-        <div class="column flex-center">
-          <span :class="numberCard.length >= 7 ? 'text-h4' : 'text-h3'">
-            {{ numberCard }}
-          </span>
-          <div
-            v-if="numberPorcent !== null"
-            class="flex no-wrap text-subtitle2"
-            :class="numberPorcent > 0 ? 'text-positive' : 'text-negative'"
-          >
-            <q-icon
-              :name="numberPorcent > 0 ? 'mdi-arrow-up' : 'mdi-arrow-down'"
-              size="18px"
-              class="q-mr-xs"
-            />
-            {{ numberPorcent + "%" }}
-          </div>
-        </div>
-        <div class="flex justify-end" style="max-width: 120px; height: 80px">
-          <slot name="chart" :color="color" :chart-data="chartData" />
-        </div>
-      </div>
-    </div>
-  </q-card>
-</template>
-
-<script setup>
-const { color, title, icon, chartData, numberCard, numberPorcent } =
-  defineProps({
-    color: {
-      type: String,
-      default: "primary",
-    },
-    title: {
-      type: String,
-      default: "Usuários",
-    },
-    icon: {
-      type: String,
-      default: "mdi-account",
-    },
-    chartData: {
-      type: Array,
-      default: () =>
-        Array.from({ length: 7 }, () => Math.floor(Math.random() * 100)),
-    },
-    numberCard: {
-      type: String,
-      default: () => Math.floor(Math.random() * 100),
-    },
-    numberPorcent: {
-      type: Number,
-      default: () => Math.ceil(Math.random() * 200 - 100),
-    },
-  });
-</script>
-<style lang="scss" scoped>
-@use "sass:map";
-@use "src/css/quasar.variables.scss";
-
-body.body--light {
-  .background {
-    background: rgba(map.get($colors, "primary"), 0.2) !important;
-  }
-}
-
-body.body--dark {
-  .background {
-    background: rgba(map.get($colors-dark, "primary"), 0.2) !important;
-  }
-}
-</style>

+ 0 - 285
src/components/charts/custom/NPSChart.vue

@@ -1,285 +0,0 @@
-<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
-<template>
-  <q-card v-bind="$attrs" class="q-px-md">
-    <q-card-section class="row justify-between">
-      <div>
-        <div class="text-h6">{{ props.title }}</div>
-        <span>{{ props.subTitle }}</span>
-      </div>
-      <q-btn
-        icon="mdi-tray-arrow-down"
-        dense
-        flat
-        class="q-my-auto"
-        @click="downloadImage"
-      />
-    </q-card-section>
-    <q-separator dark inset />
-    <div class="graph-container">
-      <div class="column">
-        <div class="col-4 row">
-          <div
-            class="bg-nps-green"
-            style="width: 1rem; min-height: max-content"
-          />
-          <div class="column q-pa-md">
-            <span class="text-bold text-h6 text-nps-green">
-              {{ $t("charts.nps.promotion_zone") }}
-            </span>
-            <span>{{ $t("charts.nps.promotion_zone_range") }}</span>
-          </div>
-        </div>
-        <div class="col-4 row">
-          <div
-            class="bg-nps-green-light"
-            style="width: 1rem; min-height: max-content"
-          />
-          <div class="column q-pa-md">
-            <span class="text-bold text-h6 text-nps-green-light">
-              {{ $t("charts.nps.quality_zone") }}
-            </span>
-            <span>{{ $t("charts.nps.quality_zone_range") }}</span>
-          </div>
-        </div>
-        <div class="col-4 row">
-          <div
-            class="bg-nps-yellow"
-            style="width: 1rem; min-height: max-content"
-          />
-          <div class="column q-pa-md">
-            <span class="text-bold text-h6 text-nps-yellow">
-              {{ $t("charts.nps.refinement_zone") }}
-            </span>
-            <span>{{ $t("charts.nps.refinement_zone_range") }}</span>
-          </div>
-        </div>
-        <div class="col-4 row">
-          <div
-            class="bg-nps-red"
-            style="width: 1rem; min-height: max-content"
-          />
-          <div class="column q-pa-md">
-            <span class="text-bold text-h6 text-nps-red">
-              {{ $t("charts.nps.critical_zone") }}</span
-            >
-            <span>{{ $t("charts.nps.critical_zone_range") }}</span>
-          </div>
-        </div>
-      </div>
-
-      <div style="display: flex; flex-direction: column; align-items: center">
-        <div style="max-height: 500px">
-          <Doughnut
-            ref="chart_ref"
-            :options="chartOptions"
-            :data="chartData"
-            :plugins="[ChartDataLabels, gaugeNeedle]"
-          />
-        </div>
-        <span>{{ titulo }}</span>
-      </div>
-
-      <div class="column q-col-gutter-md">
-        <div class="column">
-          <span>
-            {{ $t("charts.nps.promoters") }} -
-            {{ ((data.promotores / data.total) * 100).toFixed(0) }} %
-          </span>
-          <q-linear-progress
-            :value="data.promotores / data.total"
-            rounded
-            size="20px"
-            color="nps-green"
-            style="min-width: 200px"
-          />
-        </div>
-        <div class="column">
-          <span
-            >{{ $t("charts.nps.passives") }} -
-            {{ ((data.neutros / data.total) * 100).toFixed(0) }} %</span
-          >
-          <q-linear-progress
-            :value="data.neutros / data.total"
-            rounded
-            size="20px"
-            color="nps-yellow"
-            style="min-width: 200px"
-          />
-        </div>
-        <div class="column">
-          <span
-            >{{ $t("charts.nps.detractors") }} -
-            {{ ((data.detratores / data.total) * 100).toFixed(0) }} %</span
-          >
-          <q-linear-progress
-            :value="data.detratores / data.total"
-            rounded
-            size="20px"
-            color="nps-red"
-            style="min-width: 200px"
-          />
-        </div>
-      </div>
-    </div>
-  </q-card>
-</template>
-
-<script setup>
-import { ref } from "vue";
-import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
-import { Doughnut } from "vue-chartjs";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-
-const props = defineProps({
-  title: {
-    type: String,
-    required: true,
-  },
-  value: {
-    type: Number,
-    required: true,
-  },
-  subTitle: {
-    type: String,
-    default: null,
-  },
-  data: {
-    type: Object,
-    required: true,
-  },
-});
-
-const chart_ref = ref(null);
-const titulo = ref(`Valor: ${props.data?.nps}`);
-
-const gaugeNeedle = {
-  id: "gaugeNeedle",
-  afterDatasetsDraw(chart) {
-    const { ctx, data } = chart;
-    ctx.save();
-
-    const needleValue = data.datasets[0].needleValue;
-    const xCenter = chart.getDatasetMeta(0).data[0].x;
-    const yCenter = chart.getDatasetMeta(0).data[0].y;
-    const outerRadius = chart.getDatasetMeta(0).data[0].outerRadius - 40;
-    const angle = Math.PI;
-
-    let circumference =
-      (chart.getDatasetMeta(0).data[0].circumference /
-        Math.PI /
-        data.datasets[0].data[0]) *
-      needleValue;
-
-    const needleAngleValue = circumference + 1.5;
-
-    ctx.translate(xCenter, yCenter);
-    ctx.rotate(angle * needleAngleValue);
-
-    // Draw the needle
-    ctx.beginPath();
-    ctx.strokeStyle = "grey";
-    ctx.fillStyle = "grey";
-    ctx.moveTo(0 - 4, 0);
-    ctx.lineTo(0, -outerRadius);
-    ctx.lineTo(0 + 4, 0);
-    ctx.stroke();
-    ctx.fill();
-
-    ctx.beginPath();
-    ctx.arc(0, 0, 8, 0, 2 * Math.PI);
-    ctx.fillStyle = "grey";
-    ctx.fill();
-
-    ctx.restore();
-  },
-};
-
-ChartJS.register(ArcElement, Tooltip, Legend);
-
-const chartData = ref({
-  datasets: [
-    {
-      backgroundColor: ["#d43333", "#ffbe00", "#40c56c", "#0d733e"],
-      data: [100, 50, 25, 25],
-      needleValue: Number(props.data?.nps) + 100,
-      borderColor: "transparent",
-    },
-  ],
-});
-
-const chartOptions = ref({
-  rotation: 270,
-  circumference: 180,
-  cutout: "50%",
-  plugins: {
-    tooltip: {
-      enabled: false,
-    },
-    datalabels: {
-      color: "white",
-      font: {
-        size: 14,
-        weight: "bold",
-      },
-      formatter: (value, ctx) => {
-        let valor = "";
-        switch (ctx.dataIndex) {
-          case 0:
-            valor = "-100 a 0";
-            break;
-          case 1:
-            valor = "0 a 50";
-            break;
-          case 2:
-            valor = "50 a 75";
-            break;
-          case 3:
-            valor = "75 a 100";
-            break;
-          default:
-            valor = "";
-            break;
-        }
-        return valor;
-      },
-    },
-  },
-});
-
-addEventListener("resize", () => {
-  if (chart_ref.value) {
-    chart_ref.value.chart.update();
-  }
-});
-
-const downloadImage = () => {
-  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title);
-};
-</script>
-
-<style scoped>
-.graph-container {
-  display: grid;
-  grid-template-columns: 1fr 1fr 1fr;
-  align-items: center;
-  justify-content: space-around;
-  gap: 1rem;
-  width: 100%;
-  max-width: 100vw;
-  padding: 1rem;
-}
-
-@media screen and (max-width: 1024px) {
-  .graph-container {
-    grid-template-columns: 1fr 1fr;
-  }
-}
-
-@media screen and (max-width: 768px) {
-  .graph-container {
-    grid-template-columns: 1fr;
-  }
-}
-</style>

+ 0 - 201
src/components/charts/custom/SpeedometerChart.vue

@@ -1,201 +0,0 @@
-<template>
-  <q-card v-bind="$attrs" class="q-px-md full-height column justify-between">
-    <q-card-section class="q-pa-none">
-      <div class="row justify-between no-wrap items-start q-py-md q-px-sm">
-        <div>
-          <div class="text-bold text-description q-mb-sm">
-            {{ String(props.title).toLocaleUpperCase() }}
-          </div>
-          <span>{{ props.subTitle }}</span>
-        </div>
-        <q-btn
-          icon="mdi-tray-arrow-down"
-          class="q-ml-md"
-          dense
-          flat
-          @click="downloadImage"
-        />
-      </div>
-      <q-separator dark />
-    </q-card-section>
-    <div class="graph-container">
-      <Doughnut
-        ref="chart_ref"
-        :options="chartOptions"
-        :data="chartData"
-        :plugins="[ChartDataLabels, gaugeNeedle]"
-      />
-    </div>
-  </q-card>
-</template>
-
-<script setup>
-import { ref } from "vue";
-import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
-import { Doughnut } from "vue-chartjs";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-
-const props = defineProps({
-  title: {
-    type: String,
-    required: true,
-  },
-  valorAgulha: {
-    type: Number,
-    required: true,
-  },
-  subTitle: {
-    type: String,
-    default: null,
-  },
-});
-
-const chart_ref = ref(null);
-const titulo = ref(`Valor: ${props.valorAgulha}`);
-
-const gaugeNeedle = {
-  id: "gaugeNeedle",
-  afterDatasetsDraw(chart) {
-    const { ctx, data } = chart;
-    ctx.save();
-
-    const needleValue = data.datasets[0].needleValue;
-    const xCenter = chart.getDatasetMeta(0).data[0].x;
-    const yCenter = chart.getDatasetMeta(0).data[0].y;
-    const outerRadius = chart.getDatasetMeta(0).data[0].outerRadius - 40;
-    const angle = Math.PI;
-
-    let circumference =
-      (chart.getDatasetMeta(0).data[0].circumference /
-        Math.PI /
-        data.datasets[0].data[0]) *
-      needleValue;
-
-    const needleAngleValue = circumference + 1.5;
-
-    ctx.translate(xCenter, yCenter);
-    ctx.rotate(angle * needleAngleValue);
-
-    // Draw the needle
-    ctx.beginPath();
-    ctx.strokeStyle = "grey";
-    ctx.fillStyle = "grey";
-    ctx.moveTo(0 - 4, 0);
-    ctx.lineTo(0, -outerRadius);
-    ctx.lineTo(0 + 4, 0);
-    ctx.stroke();
-    ctx.fill();
-
-    ctx.beginPath();
-    ctx.arc(0, 0, 8, 0, 2 * Math.PI);
-    ctx.fillStyle = "grey";
-    ctx.fill();
-
-    ctx.restore();
-  },
-};
-
-ChartJS.register(ArcElement, Tooltip, Legend);
-
-const chartData = ref({
-  datasets: [
-    {
-      backgroundColor: [
-        "#00a550",
-        "#4dbb7e",
-        "#9ad2ad",
-        "#cce156",
-        "#fff100",
-        "#ffbe00",
-        "#ff8c00",
-        "#FC3D23",
-        "#D01616",
-        "#8A0000",
-      ],
-      data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
-      needleValue: props.valorAgulha,
-      borderColor: "transparent",
-    },
-  ],
-});
-
-const chartOptions = ref({
-  rotation: 270,
-  circumference: 180,
-  cutout: "50%",
-  plugins: {
-    tooltip: {
-      enabled: false,
-    },
-    title: {
-      display: true,
-      text: titulo.value,
-      color: "#ffffff",
-      position: "bottom",
-    },
-    datalabels: {
-      color: "black",
-      font: {
-        size: 14,
-        weight: "bold",
-      },
-      formatter: (value, ctx) => {
-        const valor = ctx.dataIndex;
-        return valor;
-      },
-    },
-  },
-});
-
-addEventListener("resize", () => {
-  if (chart_ref.value) {
-    chart_ref.value.chart.update();
-  }
-});
-
-const downloadImage = () => {
-  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title);
-};
-</script>
-
-<style scoped>
-.graph-container {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  max-height: 300px;
-}
-
-@media screen and (max-width: 1600px) {
-  .graph-container {
-    max-height: 250px;
-  }
-}
-
-@media screen and (max-width: 1360px) {
-  .graph-container {
-    max-height: 200px;
-  }
-}
-
-@media screen and (max-width: 1130px) {
-  .graph-container {
-    max-height: 175px;
-  }
-}
-
-@media screen and (max-width: 888px) {
-  .graph-container {
-    max-height: 250px;
-  }
-}
-
-@media screen and (max-width: 650px) {
-  .graph-container {
-    max-height: 300px;
-  }
-}
-</style>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 41
src/components/charts/maps/Brasil/DadosBrasil.js


+ 0 - 228
src/components/charts/maps/Brasil/MapaBrasil.vue

@@ -1,228 +0,0 @@
-<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
-<template>
-  <div>
-    <q-card style="height: 100%">
-      <q-card-section class="row justify-between">
-        <div class="column">
-          <span class="text-bold text-description q-mb-sm">
-            {{ props.title }}
-          </span>
-          <span>{{ props.subtitle }}</span>
-        </div>
-        <q-btn
-          icon="mdi-tray-arrow-down"
-          dense
-          flat
-          class="q-my-auto"
-          @click="downloadImage"
-        />
-      </q-card-section>
-
-      <q-separator inset />
-      <q-card-section>
-        <div style="display: flex; flex-direction: column">
-          <div @mouseover="hideInfoBox">
-            <svg
-              ref="ref_mapa"
-              xmlns="http://www.w3.org/2000/svg"
-              class="mapa-svg-estados"
-              viewBox="120 50 500 500"
-              @click="mapclick"
-            >
-              <g id="mapa-svg-area">
-                <MapaEstado
-                  v-for="item in items"
-                  ref="child"
-                  :key="item.uf"
-                  :item="item"
-                  :show-circle-info="showCircleInfo"
-                  :class="item.classObject"
-                  :style="item.color ? `fill: ${item.color}` : ''"
-                  @state-selected-event="onStateSelectedEvent"
-                  @state-mouse-over-event="onStateMouseOverEvent"
-                />
-              </g>
-            </svg>
-          </div>
-          <div class="bg-background--2 q-pa-md" style="border-radius: 0.5rem">
-            <div
-              v-if="selected"
-              style="display: flex; flex-direction: column; gap: 0.5rem"
-            >
-              <span>
-                <b> Estado:</b> {{ dadosEstado.name }} -
-                {{ dadosEstado.uf.toUpperCase() }}
-              </span>
-              <span><b>Participantes:</b> {{ dadosEstado.pessoas || 0 }}</span>
-            </div>
-            <div
-              v-else
-              style="display: flex; flex-direction: column; gap: 0.5rem"
-            >
-              <span>
-                <b>Selecione um estado</b>
-              </span>
-            </div>
-          </div>
-        </div>
-      </q-card-section>
-    </q-card>
-  </div>
-</template>
-
-<script setup>
-import { computed, onMounted, ref } from "vue";
-import MapaEstado from "../MapaEstado.vue";
-import { DadosBrasil } from "./DadosBrasil";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-
-const selected = ref(null);
-const showCircleInfo = ref(false);
-const infoBoxActive = ref(false);
-const infoBoxPosX = ref(0);
-const infoBoxPosY = ref(0);
-const infoBoxData = ref("");
-const child = ref(null);
-const items = ref(DadosBrasil);
-const ref_mapa = ref(null);
-
-const props = defineProps({
-  data: {
-    type: Array,
-    required: true,
-  },
-  title: {
-    type: String,
-    required: false,
-    default: "PARTICIPANTES POR ESTADO - BRASIL",
-  },
-  subtitle: {
-    type: String,
-    required: false,
-    default: "Número de participantes do evento por estado brasileiro",
-  },
-});
-
-const mapclick = (event) => {
-  // console.log("mapa-brasil: mapclick");
-  if (event.target.tagName == "svg") {
-    resetSelectionAction();
-    infoBoxActive.value = false;
-  }
-};
-const onStateSelectedEvent = (args) => {
-  // console.log("mapa-brasil: onStateSelectedEvent: ", args);
-  selected.value = args.src.item.uf;
-  resetSelectionAction();
-  args.enable();
-};
-const onStateMouseOverEvent = (args) => {
-  // console.log("mapa-brasil: onStateMouseOverEvent: ", args);
-  setInfoBoxPosition({ x: args.event.pageX, y: args.event.pageY });
-  setInfoBoxData([
-    args.src.item.name,
-    args.src.item.regional,
-    args.src.item.altText,
-  ]);
-  infoBoxActive.value = true;
-};
-const resetSelectionAction = () => {
-  // console.log("mapa-brasil: resetSelectionAction", child.value);
-  for (let index = 0; index < child.value.length; index++) {
-    child.value[index].resetAction();
-  }
-};
-const setInfoBoxPosition = (args) => {
-  infoBoxPosX.value = args.x + 30;
-  infoBoxPosY.value = args.y;
-};
-const setInfoBoxData = (args) => {
-  infoBoxData.value = args.filter(function (a) {
-    return a !== "" ? a : null;
-  });
-};
-const hideInfoBox = (args) => {
-  if (
-    ["DIV", "SVG"].indexOf(args.target.tagName.toString().toUpperCase()) > -1
-  ) {
-    infoBoxActive.value = false;
-  }
-};
-
-onMounted(() => {
-  const dataOrganizada = [...props.data].sort(
-    (a, b) => a?.total_pessoas_por_estado - b?.total_pessoas_por_estado
-  );
-  const maiorNumero =
-    dataOrganizada[dataOrganizada.length - 1]?.total_pessoas_por_estado;
-  const faixa = maiorNumero / 5;
-  const colors = ["#f6a0a1", "#f27e7f", "#ee585a", "#EB3537", "#c31315"];
-  items.value.forEach((item) => {
-    const estado = dataOrganizada.find((e) => e.estado === item.uf);
-    if (estado) {
-      const indexFaixa = Math.ceil(estado?.total_pessoas_por_estado / faixa);
-      item.color = colors[indexFaixa - 1];
-      item.pessoas = estado.total_pessoas_por_estado;
-    } else {
-      item.color = "#494949";
-      item.pessoas = 0;
-    }
-  });
-});
-
-const dadosEstado = computed(() => {
-  const estado = items.value.find((item) => item.uf === selected.value);
-  return estado;
-});
-
-const downloadImage = () => {
-  const svgString = new XMLSerializer().serializeToString(ref_mapa.value);
-  const base64String = btoa(svgString);
-  base64ToJPEG(base64String, props.title);
-};
-</script>
-
-<style lang="scss">
-svg text {
-  fill: var(--default-stroke);
-  font-family: monospace;
-}
-
-.mapa-svg-estados {
-  fill: var(--default-fill);
-  -webkit-transition: 0.8s ease;
-  -moz-transition: 0.8s ease;
-  -ms-transition: 0.8s ease;
-  -o-transition: 0.8s ease;
-  transition: 0.8s ease;
-  stroke-dasharray: 180%;
-  stroke-dashoffset: -120%;
-  stroke-width: 1px;
-  stroke: var(--default-stroke);
-  text {
-    fill: var(--default-stroke);
-    stroke: none !important;
-  }
-}
-
-.mapa-svg-estados:hover {
-  cursor: pointer;
-  fill: #8d1012 !important;
-}
-
-.mapa-svg-estados-active {
-  cursor: pointer;
-  stroke: #ffffff;
-  fill: #ffc712 !important;
-  stroke-dashoffset: 0%;
-  transition: 0.8s ease;
-  -webkit-transition: 0.8s ease;
-  -moz-transition: 0.8s ease;
-  -ms-transition: 0.8s ease;
-  -o-transition: 0.8s ease;
-  text {
-    fill: #ffffff;
-    stroke: none !important;
-  }
-}
-</style>

+ 0 - 110
src/components/charts/maps/MapaEstado.vue

@@ -1,110 +0,0 @@
-<template>
-  <g
-    ref="chart_ref"
-    :class="[classObject]"
-    :data-regional="item.regional"
-    @click="clickAction"
-  >
-    <path :d="item.svgData" />
-    <text
-      :transform="item.textData"
-      class="item text-weight-medium"
-      style="font-size: 10px"
-      fill="white"
-    >
-      {{ item.pessoas || 0 }}
-    </text>
-    <circle
-      v-if="showCircleInfo"
-      :cy="item.circleData.cy"
-      :cx="item.circleData.cx"
-      r="10"
-      :class="[counterClassObject]"
-    />
-  </g>
-</template>
-
-<script setup>
-import { computed, onMounted, ref } from "vue";
-
-const props = defineProps({
-  item: {
-    type: Object,
-    default: () => ({
-      altText: "--",
-    }),
-  },
-  showCircleInfo: {
-    type: Boolean,
-    default: true,
-  },
-});
-
-const emit = defineEmits(["stateSelectedEvent", "stateMouseOverEvent"]);
-
-const active = ref(false);
-const counterActive = ref(false);
-const altText = ref(props.item.altText || "-");
-const chart_ref = ref(null);
-
-const classObject = computed(() => {
-  return active.value ? ["mapa-svg-estados-active"] : ["mapa-svg-estados"];
-});
-
-const counterClassObject = computed(() => {
-  return counterActive.value ? ["sphere active"] : ["sphere"];
-});
-
-// const pad = (value) => {
-//   if (!value) return "";
-//   return ("000000000" + value).slice(-2);
-// };
-
-const clickAction = () => {
-  // console.log("mapa-estado: clickAction()");
-  emit("stateSelectedEvent", {
-    src: {
-      item: props.item,
-    },
-    enable,
-  });
-};
-const resetAction = () => {
-  disable();
-};
-const disable = () => {
-  // console.log("mapa-estado: disabled ", props.item.uf);
-  active.value = false;
-  hideCounter();
-};
-
-const enable = () => {
-  // console.log("mapa-estado: enable ");
-  active.value = true;
-  showCounter();
-};
-const setAltText = (val) => {
-  altText.value = val;
-};
-const hideCounter = () => {
-  counterActive.value = false;
-};
-const showCounter = () => {
-  counterActive.value = true;
-};
-
-onMounted(() => {
-  altText.value = props.item.altText;
-});
-
-defineExpose({
-  resetAction,
-  setAltText,
-});
-</script>
-
-<style>
-.item {
-  fill: rgb(240, 221, 221);
-}
-</style>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 14
src/components/charts/maps/Paraguai/DadosParaguai.js


+ 0 - 225
src/components/charts/maps/Paraguai/MapaParaguai.vue

@@ -1,225 +0,0 @@
-<!-- eslint-disable @intlify/vue-i18n/no-raw-text -->
-<template>
-  <div>
-    <q-card style="height: 100%">
-      <q-card-section class="row justify-between">
-        <div class="column">
-          <span class="text-bold text-description q-mb-sm">
-            {{ props.title }}
-          </span>
-          <span>{{ props.subtitle }}</span>
-        </div>
-        <q-btn
-          icon="mdi-tray-arrow-down"
-          dense
-          flat
-          class="q-my-auto"
-          @click="downloadImage"
-        />
-      </q-card-section>
-
-      <q-separator inset />
-      <q-card-section>
-        <div style="display: flex; flex-direction: column">
-          <div @mouseover="hideInfoBox">
-            <svg
-              ref="ref_mapa"
-              xmlns="http://www.w3.org/2000/svg"
-              class="mapa-svg-estados"
-              viewBox="-100 -60 1250 1250"
-              @click="mapclick"
-            >
-              <g id="mapa-svg-area">
-                <MapaEstado
-                  v-for="item in items"
-                  ref="child"
-                  :key="item.id"
-                  :item="item"
-                  :show-circle-info="showCircleInfo"
-                  :class="item.classObject"
-                  :style="item.color ? `fill: ${item.color}` : ''"
-                  @state-selected-event="onStateSelectedEvent"
-                  @state-mouse-over-event="onStateMouseOverEvent"
-                ></MapaEstado>
-              </g>
-            </svg>
-          </div>
-          <div class="bg-background--2 q-pa-md" style="border-radius: 0.5rem">
-            <div
-              v-if="selected"
-              style="display: flex; flex-direction: column; gap: 0.5rem"
-            >
-              <span> <b> Estado:</b> {{ dadosEstado.name }} </span>
-              <span><b>Participantes:</b> {{ dadosEstado.pessoas || 0 }}</span>
-            </div>
-            <div
-              v-else
-              style="display: flex; flex-direction: column; gap: 0.5rem"
-            >
-              <span>
-                <b>Selecione um estado</b>
-              </span>
-            </div>
-          </div>
-        </div>
-      </q-card-section>
-    </q-card>
-  </div>
-</template>
-
-<script setup>
-import { computed, onMounted, ref } from "vue";
-import MapaEstado from "../MapaEstado.vue";
-import { DadosParaguai } from "./DadosParaguai";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-
-const selected = ref(null);
-const showCircleInfo = ref(false);
-const infoBoxActive = ref(false);
-const infoBoxPosX = ref(0);
-const infoBoxPosY = ref(0);
-const infoBoxData = ref("");
-const child = ref(null);
-const items = ref(DadosParaguai);
-const ref_mapa = ref(null);
-
-const props = defineProps({
-  data: {
-    type: Array,
-    required: true,
-  },
-  title: {
-    type: String,
-    required: false,
-    default: "PARTICIPANTES POR DEPARTAMENTO - PARAGUAI",
-  },
-  subtitle: {
-    type: String,
-    required: false,
-    default: "Número de participantes do evento por departamento do Paraguai",
-  },
-});
-
-const mapclick = (event) => {
-  // console.log("mapa-brasil: mapclick");
-  if (event.target.tagName == "svg") {
-    resetSelectionAction();
-    infoBoxActive.value = false;
-  }
-};
-const onStateSelectedEvent = (args) => {
-  // console.log("mapa-brasil: onStateSelectedEvent: ", args);
-  selected.value = args.src.item.id;
-  resetSelectionAction();
-  args.enable();
-};
-const onStateMouseOverEvent = (args) => {
-  // console.log("mapa-brasil: onStateMouseOverEvent: ", args);
-  setInfoBoxPosition({ x: args.event.pageX, y: args.event.pageY });
-  setInfoBoxData([
-    args.src.item.name,
-    args.src.item.regional,
-    args.src.item.altText,
-  ]);
-  infoBoxActive.value = true;
-};
-const resetSelectionAction = () => {
-  // console.log("mapa-brasil: resetSelectionAction", child.value);
-  for (let index = 0; index < child.value.length; index++) {
-    child.value[index].resetAction();
-  }
-};
-const setInfoBoxPosition = (args) => {
-  infoBoxPosX.value = args.x + 30;
-  infoBoxPosY.value = args.y;
-};
-const setInfoBoxData = (args) => {
-  infoBoxData.value = args.filter(function (a) {
-    return a !== "" ? a : null;
-  });
-};
-const hideInfoBox = (args) => {
-  if (
-    ["DIV", "SVG"].indexOf(args.target.tagName.toString().toUpperCase()) > -1
-  ) {
-    infoBoxActive.value = false;
-  }
-};
-
-onMounted(() => {
-  const dataOrganizada = [...props.data].sort(
-    (a, b) => a?.total_pessoas_por_estado - b?.total_pessoas_por_estado
-  );
-  const maiorNumero =
-    dataOrganizada[dataOrganizada.length - 1]?.total_pessoas_por_estado;
-  const faixa = maiorNumero / 5;
-  const colors = ["#f6a0a1", "#f27e7f", "#ee585a", "#EB3537", "#c31315"];
-  items.value.forEach((item) => {
-    const estado = dataOrganizada.find((e) => e.estado_id === item.id);
-    if (estado) {
-      const indexFaixa = Math.ceil(estado?.total_pessoas_por_estado / faixa);
-      item.color = colors[indexFaixa - 1];
-      item.pessoas = estado.total_pessoas_por_estado;
-    } else {
-      item.color = "#494949";
-      item.pessoas = 0;
-    }
-  });
-});
-
-const dadosEstado = computed(() => {
-  const estado = items.value.find((item) => item.id === selected.value);
-  return estado;
-});
-
-const downloadImage = () => {
-  const svgString = new XMLSerializer().serializeToString(ref_mapa.value);
-  const base64String = btoa(svgString);
-  base64ToJPEG(base64String, props.title);
-};
-</script>
-
-<style lang="scss">
-svg text {
-  fill: var(--default-stroke);
-  font-family: monospace;
-}
-
-.mapa-svg-estados {
-  fill: var(--default-fill);
-  -webkit-transition: 0.8s ease;
-  -moz-transition: 0.8s ease;
-  -ms-transition: 0.8s ease;
-  -o-transition: 0.8s ease;
-  transition: 0.8s ease;
-  stroke-dasharray: 180%;
-  stroke-dashoffset: -120%;
-  stroke-width: 1px;
-  stroke: var(--default-stroke);
-  text {
-    fill: var(--default-stroke);
-    stroke: none !important;
-  }
-}
-
-.mapa-svg-estados:hover {
-  cursor: pointer;
-  fill: #8d1012 !important;
-}
-
-.mapa-svg-estados-active {
-  cursor: pointer;
-  stroke: #ffffff;
-  fill: #ffc712 !important;
-  stroke-dashoffset: 0%;
-  transition: 0.8s ease;
-  -webkit-transition: 0.8s ease;
-  -moz-transition: 0.8s ease;
-  -ms-transition: 0.8s ease;
-  -o-transition: 0.8s ease;
-  text {
-    fill: #ffffff;
-    stroke: none !important;
-  }
-}
-</style>

+ 0 - 101
src/components/charts/mini/MiniBarChart.vue

@@ -1,101 +0,0 @@
-<template>
-  <Bar
-    :id="id"
-    ref="chart_ref"
-    :options="chartOptions"
-    :data="computedChartData"
-  />
-</template>
-
-<script setup>
-import {
-  Chart as ChartJS,
-  CategoryScale,
-  LinearScale,
-  BarElement,
-  Tooltip,
-} from "chart.js";
-import { computed, useTemplateRef } from "vue";
-import { Bar } from "vue-chartjs";
-import { getCssVar } from "quasar";
-
-ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
-
-const chart_ref = useTemplateRef(null);
-
-const { data, barColor, horizontal, showTooltip } = defineProps({
-  data: {
-    type: Array,
-    required: true,
-  },
-  barColor: {
-    type: String,
-    default: () => getCssVar("primary"),
-  },
-  horizontal: {
-    type: Boolean,
-    default: false,
-  },
-  showTooltip: {
-    type: Boolean,
-    default: true,
-  },
-});
-
-const chartOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-  indexAxis: horizontal ? "y" : "x",
-
-  plugins: {
-    legend: {
-      display: false,
-    },
-    tooltip: {
-      enabled: showTooltip,
-      displayColors: false,
-      callbacks: {
-        label: (context) => `${context.raw}`,
-      },
-    },
-  },
-
-  scales: {
-    x: {
-      display: false,
-      grid: {
-        display: false,
-      },
-    },
-    y: {
-      display: false,
-      grid: {
-        display: false,
-      },
-      beginAtZero: true,
-    },
-  },
-
-  animation: {
-    duration: 750,
-    easing: "easeOutQuad",
-  },
-}));
-
-const computedChartData = computed(() => ({
-  labels: Array(data.length).fill(""),
-  datasets: [
-    {
-      data: data,
-      backgroundColor: barColor,
-      borderRadius: 2,
-      barThickness: 8,
-      maxBarThickness: 10,
-    },
-  ],
-}));
-
-defineExpose({
-  chart_ref,
-});
-</script>

+ 0 - 116
src/components/charts/mini/MiniLineChart.vue

@@ -1,116 +0,0 @@
-<template>
-  <Line ref="chart_ref" :options="chartOptions" :data="computedChartData" />
-</template>
-
-<script setup>
-import {
-  Chart as ChartJS,
-  CategoryScale,
-  LinearScale,
-  LineElement,
-  PointElement,
-  Tooltip,
-  Filler,
-} from "chart.js";
-import { computed, useTemplateRef } from "vue";
-import { Line } from "vue-chartjs";
-import { getCssVar } from "quasar";
-
-ChartJS.register(
-  CategoryScale,
-  LinearScale,
-  LineElement,
-  PointElement,
-  Tooltip,
-  Filler,
-);
-
-const chart_ref = useTemplateRef(null);
-
-const { data, lineColor, fillColor, showTooltip, showPoints } = defineProps({
-  data: {
-    type: Array,
-    required: true,
-  },
-  lineColor: {
-    type: String,
-    default: () => getCssVar("primary"),
-  },
-  fillColor: {
-    type: String,
-    default: "rgba(25, 118, 210, 0.1)",
-  },
-  showTooltip: {
-    type: Boolean,
-    default: true,
-  },
-  showPoints: {
-    type: Boolean,
-    default: false,
-  },
-});
-
-const chartOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-
-  plugins: {
-    legend: {
-      display: false,
-    },
-    tooltip: {
-      enabled: showTooltip,
-      displayColors: false,
-      callbacks: {
-        label: (context) => `${context.raw}`,
-      },
-    },
-  },
-
-  scales: {
-    x: {
-      display: false,
-      grid: { display: false },
-    },
-    y: {
-      display: false,
-      grid: { display: false },
-      beginAtZero: true,
-    },
-  },
-
-  elements: {
-    line: {
-      tension: 0.4,
-      borderWidth: 2,
-    },
-    point: {
-      radius: showPoints ? 3 : 0,
-      hitRadius: 5,
-      borderWidth: 0,
-      backgroundColor: showPoints ? lineColor : "rgba(0,0,0,0)",
-    },
-  },
-
-  animation: {
-    duration: 750,
-    easing: "easeOutQuad",
-  },
-}));
-
-const computedChartData = computed(() => ({
-  labels: Array(data?.length)?.fill(""),
-  datasets: [
-    {
-      data: data,
-      borderColor: lineColor,
-      backgroundColor: fillColor,
-      fill: true,
-    },
-  ],
-}));
-
-defineExpose({
-  chart_ref,
-});
-</script>

+ 0 - 267
src/components/charts/normal/BarChart.vue

@@ -1,267 +0,0 @@
-<template>
-  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
-    <q-resize-observer @resize="onResize" />
-    <div v-if="hasData" class="chart-container">
-      <Bar
-        ref="chart_ref"
-        :options="chartBarOptions"
-        :data="chartBarData"
-        :plugins="[ChartDataLabels]"
-      />
-    </div>
-    <div v-else class="no-data-container">
-      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from "vue";
-import { Bar } from "vue-chartjs";
-import {
-  Chart as ChartJS,
-  Title,
-  Tooltip,
-  Legend,
-  BarElement,
-  CategoryScale,
-  LinearScale,
-} from "chart.js";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-import { useQuasar, colors, getCssVar } from "quasar";
-
-ChartJS.register(
-  Title,
-  Tooltip,
-  Legend,
-  BarElement,
-  CategoryScale,
-  LinearScale,
-);
-
-const $q = useQuasar();
-const chart_ref = ref(null);
-const { lighten } = colors;
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({
-      chart_data: [],
-      current_total: 0,
-    }),
-  },
-  dataSetLabel: {
-    type: String,
-    default: "Quantidade",
-  },
-  labelX: {
-    type: String,
-    default: "Categorias",
-  },
-  labelY: {
-    type: String,
-    default: "Valores",
-  },
-  showLegend: {
-    type: Boolean,
-    default: false,
-  },
-  title: {
-    type: String,
-    default: "Título",
-  },
-  backgroundColors: {
-    type: Array,
-    default: null,
-  },
-});
-
-const onResize = () => {
-  if (chart_ref.value?.chart) {
-    setTimeout(() => {
-      chart_ref.value.chart.resize();
-    }, 50);
-  }
-};
-
-const textColor = computed(() => {
-  return $q.dark.isActive ? "text-white" : "text-black";
-});
-
-const labelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const gridColor = computed(() => {
-  return $q.dark.isActive ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
-});
-
-const dataLabelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const hasData = computed(() => {
-  return props.data?.chart_data && props.data.chart_data.length > 0;
-});
-
-const chartLabels = computed(() => {
-  return props.data?.chart_data?.map((item) => item.label) || [];
-});
-
-const chartValues = computed(() => {
-  return props.data?.chart_data?.map((item) => item.value) || [];
-});
-
-const chartThemeColors = computed(() => {
-  if (props.backgroundColors) {
-    return props.backgroundColors;
-  }
-
-  const primaryColor = getCssVar("primary");
-  if (!primaryColor) return [];
-
-  const numColors = chartValues.value.length;
-  const step = numColors > 0 ? 50 / numColors : 0;
-  return Array.from({ length: numColors }, (_, i) =>
-    lighten(primaryColor, i * step),
-  );
-});
-
-const chartBarData = computed(() => ({
-  labels: chartLabels.value,
-  datasets: [
-    {
-      label: props.dataSetLabel,
-      data: chartValues.value,
-      backgroundColor: chartThemeColors.value,
-      borderColor: chartThemeColors.value,
-      borderWidth: 1,
-    },
-  ],
-}));
-
-const chartBarOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-  plugins: {
-    legend: {
-      display: props.showLegend,
-      position: "top",
-      labels: {
-        color: labelColor.value,
-        font: {
-          size: 14,
-        },
-      },
-    },
-    datalabels: {
-      color: dataLabelColor.value,
-      anchor: "end",
-      align: "top",
-      offset: 4,
-      font: {
-        size: 12,
-        weight: "bold",
-      },
-      formatter: (value) => {
-        return value > 0 ? value : "";
-      },
-    },
-    tooltip: {
-      backgroundColor: $q.dark.isActive
-        ? "rgba(0, 0, 0, 0.8)"
-        : "rgba(255, 255, 255, 0.9)",
-      titleColor: labelColor.value,
-      bodyColor: labelColor.value,
-      borderColor: labelColor.value,
-      borderWidth: 1,
-    },
-  },
-  scales: {
-    x: {
-      display: true,
-      title: {
-        display: !!props.labelX,
-        text: props.labelX,
-        color: labelColor.value,
-        font: {
-          size: 14,
-        },
-      },
-      grid: {
-        color: gridColor.value,
-        tickColor: gridColor.value,
-      },
-      ticks: {
-        color: labelColor.value,
-        font: {
-          size: 12,
-        },
-      },
-    },
-    y: {
-      display: true,
-      title: {
-        display: !!props.labelY,
-        text: props.labelY,
-        color: labelColor.value,
-        font: {
-          size: 14,
-        },
-      },
-      suggestedMin: 0,
-      grid: {
-        color: gridColor.value,
-        tickColor: gridColor.value,
-      },
-      ticks: {
-        color: labelColor.value,
-        font: {
-          size: 12,
-        },
-        stepSize: 1,
-      },
-    },
-  },
-}));
-
-const downloadImage = () => {
-  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title || "bar-chart");
-};
-
-defineExpose({
-  downloadImage,
-  chart_ref,
-});
-</script>
-
-<style scoped>
-.chart-wrapper {
-  position: relative;
-}
-
-.chart-container {
-  position: absolute;
-  top: 10px;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  width: 100%;
-  height: 100%;
-}
-
-.no-data-container {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 1.5rem;
-}
-</style>

+ 0 - 204
src/components/charts/normal/DoughnutChart.vue

@@ -1,204 +0,0 @@
-<template>
-  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
-    <q-resize-observer @resize="onResize" />
-    <div v-if="hasData" class="chart-container">
-      <Doughnut
-        ref="chart_ref"
-        :options="chartPieOptions"
-        :data="chartPieData"
-        :plugins="[ChartDataLabels]"
-      />
-    </div>
-    <div v-else class="no-data-container">
-      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from "vue";
-import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
-import { Doughnut } from "vue-chartjs";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-import { useQuasar, colors, getCssVar } from "quasar";
-
-ChartJS.register(ArcElement, Tooltip, Legend);
-
-const $q = useQuasar();
-const { lighten } = colors;
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({
-      chart_data: [],
-    }),
-  },
-  backgroundColors: {
-    type: Array,
-    default: null,
-  },
-  title: {
-    type: String,
-    default: "doughnut-chart",
-  },
-  dataSetLabel: {
-    type: String,
-    default: "Dados",
-  },
-});
-
-const chart_ref = ref(null);
-
-const onResize = () => {
-  if (chart_ref.value?.chart) {
-    setTimeout(() => {
-      chart_ref.value.chart.resize();
-    }, 50);
-  }
-};
-
-const textColor = computed(() => {
-  return $q.dark.isActive ? "text-white" : "text-black";
-});
-
-const labelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const dataLabelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const hasData = computed(() => {
-  return props.data?.chart_data && props.data.chart_data.length > 0;
-});
-
-const chartLabels = computed(() => {
-  return props.data?.chart_data?.map((item) => item.label) || [];
-});
-
-const chartValues = computed(() => {
-  return props.data?.chart_data?.map((item) => item.value) || [];
-});
-
-const chartPercentages = computed(() => {
-  const total = props.data?.current_total || 0;
-  if (total === 0) return [];
-
-  return (
-    props.data?.chart_data?.map((item) =>
-      Math.round((item.value / total) * 100),
-    ) || []
-  );
-});
-
-const chartThemeColors = computed(() => {
-  if (props.backgroundColors) {
-    return props.backgroundColors;
-  }
-
-  const primaryColor = getCssVar("primary");
-  if (!primaryColor) return [];
-
-  const numColors = chartValues.value.length;
-  const step = numColors > 0 ? 50 / numColors : 0;
-  return Array.from({ length: numColors }, (_, i) =>
-    lighten(primaryColor, i * step),
-  );
-});
-
-const chartPieData = computed(() => ({
-  labels: chartLabels.value,
-  datasets: [
-    {
-      label: props.dataSetLabel,
-      data: chartPercentages.value,
-      backgroundColor: chartThemeColors.value,
-    },
-  ],
-}));
-
-const chartPieOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-  plugins: {
-    legend: {
-      position: "bottom",
-      labels: {
-        color: labelColor.value,
-        font: {
-          size: 12,
-        },
-        padding: 20,
-      },
-    },
-    datalabels: {
-      color: dataLabelColor.value,
-      font: {
-        size: 12,
-        weight: "bold",
-      },
-      formatter: (value) => {
-        return value > 0 ? value + "%" : "";
-      },
-    },
-    tooltip: {
-      backgroundColor: $q.dark.isActive
-        ? "rgba(0, 0, 0, 0.8)"
-        : "rgba(255, 255, 255, 0.9)",
-      titleColor: labelColor.value,
-      bodyColor: labelColor.value,
-      borderColor: labelColor.value,
-      borderWidth: 1,
-      callbacks: {
-        label: (context) => {
-          const label = context.label || "";
-          const percentage = context.parsed;
-          const actualValue = chartValues.value[context.dataIndex];
-          return `${label}: ${actualValue} (${percentage}%)`;
-        },
-      },
-    },
-  },
-}));
-
-const downloadImage = () => {
-  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title || "pie-chart");
-};
-
-defineExpose({
-  downloadImage,
-  chart_ref,
-});
-</script>
-
-<style scoped>
-.chart-wrapper {
-  position: relative;
-}
-
-.chart-container {
-  position: absolute;
-  top: 10px;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  width: 100%;
-  height: 100%;
-}
-
-.no-data-container {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 1.5rem;
-}
-</style>

+ 0 - 265
src/components/charts/normal/LineChart.vue

@@ -1,265 +0,0 @@
-<template>
-  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
-    <q-resize-observer @resize="onResize" />
-    <div v-if="hasData" class="chart-container">
-      <Line
-        ref="chart_ref"
-        :options="chartLineOptions"
-        :data="chartLineData"
-        :plugins="[ChartDataLabels]"
-      />
-    </div>
-    <div v-else class="no-data-container">
-      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from "vue";
-import { Line } from "vue-chartjs";
-import {
-  Chart as ChartJS,
-  Title,
-  Tooltip,
-  Legend,
-  LineElement,
-  PointElement,
-  CategoryScale,
-  LinearScale,
-} from "chart.js";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-import { useQuasar, getCssVar, colors } from "quasar";
-
-ChartJS.register(
-  Title,
-  Tooltip,
-  Legend,
-  LineElement,
-  PointElement,
-  CategoryScale,
-  LinearScale,
-);
-
-const $q = useQuasar();
-const { lighten } = colors;
-const chart_ref = ref(null);
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({
-      chart_data: [],
-    }),
-  },
-  title: {
-    type: String,
-    default: "",
-  },
-  dataSetLabel: {
-    type: String,
-    default: "Valores",
-  },
-  labelX: {
-    type: String,
-    default: "Categorias",
-  },
-  labelY: {
-    type: String,
-    default: "Valores",
-  },
-  backgroundColors: {
-    type: Array,
-    default: null,
-  },
-});
-
-const onResize = () => {
-  if (chart_ref.value?.chart) {
-    setTimeout(() => {
-      chart_ref.value.chart.resize();
-    }, 50);
-  }
-};
-
-const textColor = computed(() => {
-  return $q.dark.isActive ? "text-white" : "text-black";
-});
-
-const labelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const gridColor = computed(() => {
-  return $q.dark.isActive ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
-});
-
-const dataLabelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const hasData = computed(() => {
-  return props.data?.chart_data && props.data.chart_data.length > 0;
-});
-
-const chartLabels = computed(() => {
-  return props.data?.chart_data?.map((item) => item.label) || [];
-});
-
-const chartValues = computed(() => {
-  return props.data?.chart_data?.map((item) => item.value) || [];
-});
-
-const chartThemeColors = computed(() => {
-  if (props.backgroundColors) {
-    return props.backgroundColors;
-  }
-
-  const primaryColor = getCssVar("primary");
-  if (!primaryColor) return [];
-
-  const numColors = chartValues.value.length;
-  const step = numColors > 0 ? 50 / numColors : 0;
-  return Array.from({ length: numColors }, (_, i) =>
-    lighten(primaryColor, i * step),
-  );
-});
-
-const chartLineData = computed(() => ({
-  labels: chartLabels.value,
-  datasets: [
-    {
-      label: props.dataSetLabel,
-      data: chartValues.value,
-      borderColor: chartThemeColors.value[0],
-      backgroundColor: chartThemeColors.value,
-      fill: false,
-      cubicInterpolationMode: "monotone",
-      tension: 0.4,
-    },
-  ],
-}));
-
-const chartLineOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-  plugins: {
-    legend: {
-      display: false,
-    },
-    title: {
-      display: !!props.title,
-      text: props.title,
-      color: labelColor.value,
-      font: {
-        size: 16,
-      },
-    },
-    datalabels: {
-      color: dataLabelColor.value,
-      anchor: "end",
-      align: "top",
-      offset: 4,
-      font: {
-        size: 12,
-        weight: "bold",
-      },
-      formatter: (value) => {
-        return value > 0 ? value : "";
-      },
-    },
-    tooltip: {
-      backgroundColor: $q.dark.isActive
-        ? "rgba(0, 0, 0, 0.8)"
-        : "rgba(255, 255, 255, 0.9)",
-      titleColor: labelColor.value,
-      bodyColor: labelColor.value,
-      borderColor: labelColor.value,
-      borderWidth: 1,
-    },
-  },
-  interaction: {
-    intersect: false,
-  },
-  scales: {
-    x: {
-      display: true,
-      title: {
-        display: !!props.labelX,
-        text: props.labelX,
-        color: labelColor.value,
-        font: {
-          size: 14,
-        },
-      },
-      grid: {
-        color: gridColor.value,
-        tickColor: gridColor.value,
-      },
-      ticks: {
-        color: labelColor.value,
-      },
-    },
-    y: {
-      display: true,
-      title: {
-        display: !!props.labelY,
-        text: props.labelY,
-        color: labelColor.value,
-        font: {
-          size: 14,
-        },
-      },
-      suggestedMin: 0,
-      grid: {
-        color: gridColor.value,
-        tickColor: gridColor.value,
-      },
-      ticks: {
-        color: labelColor.value,
-        stepSize: 1,
-      },
-    },
-  },
-}));
-
-const downloadImage = () => {
-  if (!chart_ref.value?.chart) return;
-  const image = chart_ref.value.chart.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title || "line-chart");
-};
-
-defineExpose({
-  downloadImage,
-  chart_ref,
-});
-</script>
-
-<style scoped>
-.chart-wrapper {
-  position: relative;
-}
-
-.chart-container {
-  position: absolute;
-  top: 10px;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  width: 100%;
-  height: 100%;
-}
-
-.no-data-container {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 1.5rem;
-}
-</style>

+ 0 - 205
src/components/charts/normal/PieChart.vue

@@ -1,205 +0,0 @@
-<template>
-  <div v-bind="$attrs" class="chart-wrapper full-width full-height">
-    <q-resize-observer @resize="onResize" />
-    <div v-if="hasData" class="chart-container">
-      <Pie
-        ref="chart_ref"
-        :options="chartPieOptions"
-        :data="chartPieData"
-        :plugins="[ChartDataLabels]"
-      />
-    </div>
-    <div v-else class="no-data-container">
-      <span :class="textColor">{{ $t("http.errors.no_records_found") }}</span>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed } from "vue";
-import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
-import { Pie } from "vue-chartjs";
-import ChartDataLabels from "chartjs-plugin-datalabels";
-import { base64ToJPEG } from "src/helpers/convertBase64Image";
-import { useQuasar, getCssVar, colors } from "quasar";
-
-ChartJS.register(ArcElement, Tooltip, Legend);
-
-const $q = useQuasar();
-const { lighten } = colors;
-
-const props = defineProps({
-  data: {
-    type: Object,
-    default: () => ({
-      chart_data: [],
-      current_total: 0,
-    }),
-  },
-  dataSetLabel: {
-    type: String,
-    default: "Dados",
-  },
-  title: {
-    type: String,
-    default: "Título",
-  },
-  backgroundColors: {
-    type: Array,
-    default: null,
-  },
-});
-
-const chart_ref = ref(null);
-
-const onResize = () => {
-  if (chart_ref.value?.chart) {
-    setTimeout(() => {
-      chart_ref.value.chart.resize();
-    }, 50);
-  }
-};
-
-const textColor = computed(() => {
-  return $q.dark.isActive ? "text-white" : "text-black";
-});
-
-const labelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const dataLabelColor = computed(() => {
-  return $q.dark.isActive ? "#ffffff" : "#000000";
-});
-
-const hasData = computed(() => {
-  return props.data?.chart_data && props.data.chart_data.length > 0;
-});
-
-const chartLabels = computed(() => {
-  return props.data?.chart_data?.map((item) => item.label) || [];
-});
-
-const chartValues = computed(() => {
-  return props.data?.chart_data?.map((item) => item.value) || [];
-});
-
-const chartPercentages = computed(() => {
-  const total = props.data?.current_total || 0;
-  if (total === 0) return [];
-
-  return (
-    props.data?.chart_data?.map((item) =>
-      Math.round((item.value / total) * 100),
-    ) || []
-  );
-});
-
-const chartThemeColors = computed(() => {
-  if (props.backgroundColors) {
-    return props.backgroundColors;
-  }
-
-  const primaryColor = getCssVar("primary");
-  if (!primaryColor) return [];
-
-  const numColors = chartValues.value.length;
-  const step = numColors > 0 ? 50 / numColors : 0;
-  return Array.from({ length: numColors }, (_, i) =>
-    lighten(primaryColor, i * step),
-  );
-});
-
-const chartPieData = computed(() => ({
-  labels: chartLabels.value,
-  datasets: [
-    {
-      label: props.dataSetLabel,
-      data: chartPercentages.value,
-      backgroundColor: chartThemeColors.value,
-    },
-  ],
-}));
-
-const chartPieOptions = computed(() => ({
-  responsive: true,
-  maintainAspectRatio: false,
-  plugins: {
-    legend: {
-      position: "bottom",
-      labels: {
-        color: labelColor.value,
-        font: {
-          size: 12,
-        },
-        padding: 20,
-      },
-    },
-    datalabels: {
-      color: dataLabelColor.value,
-      font: {
-        size: 12,
-        weight: "bold",
-      },
-      formatter: (value) => {
-        return value > 0 ? value + "%" : "";
-      },
-    },
-    tooltip: {
-      backgroundColor: $q.dark.isActive
-        ? "rgba(0, 0, 0, 0.8)"
-        : "rgba(255, 255, 255, 0.9)",
-      titleColor: labelColor.value,
-      bodyColor: labelColor.value,
-      borderColor: labelColor.value,
-      borderWidth: 1,
-      callbacks: {
-        label: (context) => {
-          const label = context.label || "";
-          const percentage = context.parsed;
-          const actualValue = chartValues.value[context.dataIndex];
-          return `${label}: ${actualValue} (${percentage}%)`;
-        },
-      },
-    },
-  },
-}));
-
-const downloadImage = () => {
-  const image = chart_ref.value.chart?.toBase64Image("image/jpeg", 1);
-  base64ToJPEG(image, props.title || "pie-chart");
-};
-
-defineExpose({
-  downloadImage,
-  chart_ref,
-});
-</script>
-
-<style scoped>
-.chart-wrapper {
-  position: relative;
-}
-
-.chart-container {
-  position: absolute;
-  top: 10px;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  width: 100%;
-  height: 100%;
-}
-
-.no-data-container {
-  position: absolute;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 1.5rem;
-}
-</style>

+ 5 - 13
src/components/dashboard/DashboardHeaderBar.vue

@@ -4,12 +4,12 @@
     <div class="col column q-gutter-y-xs">
       <div class="row items-center q-gutter-x-xs">
         <q-icon name="mdi-star" color="warning" size="14px" />
-        <span class="dashboard-metric-value">{{ data?.rating != null ? Number(data.rating).toFixed(1).replace('.', ',') : '-' }}</span>
-        <span class="dashboard-metric-meta">({{ data?.total_ratings ?? 0 }})</span>
+        <span class="dashboard-metric-value font10 fontregular">{{ data?.rating != null ? Number(data.rating).toFixed(1).replace('.', ',') : '-' }}</span>
+        <span class="dashboard-metric-meta font10 fontregular">({{ data?.total_ratings ?? 0 }})</span>
       </div>
       <div class="row items-center q-gutter-x-xs">
         <q-icon name="mdi-broom" color="secondary" size="14px" />
-        <span class="dashboard-metric-value">{{ data?.total_services ?? 0 }}</span>
+        <span class="dashboard-metric-value font10 fontregular">{{ data?.total_services ?? 0 }}</span>
       </div>
     </div>
 
@@ -38,7 +38,7 @@
       floating
       rounded
       color="pink"
-      class="notification-badge"
+      class="notification-badge font10 fontregular"
     >
       {{ unreadNotifications }}
     </q-badge>
@@ -84,7 +84,7 @@ const goToNotifications = () => {
 
 <style scoped lang="scss">
 .dashboard-header {
-  padding-top: calc(env(safe-area-inset-top) + 8px);
+  padding-top: 8px;
   width: 100%;
   box-sizing: border-box;
 }
@@ -93,16 +93,10 @@ const goToNotifications = () => {
   height: 32px;
 }
 .dashboard-metric-value {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 700;
   color: #3a3a4a;
   line-height: 1;
 }
 .dashboard-metric-meta {
-  font-family: "Inter", sans-serif;
-  font-size: 10px;
-  font-weight: 400;
   color: #999;
   line-height: 1;
 }
@@ -110,7 +104,5 @@ const goToNotifications = () => {
 .notification-badge {
   min-width: 16px;
   height: 16px;
-  font-size: 10px;
-  font-weight: 700;
 }
 </style>

+ 24 - 41
src/components/dashboard/DashboardNextSchedules.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="q-mx-md q-mb-md">
-    <div class="section-title gradient-diarista q-mb-sm">{{ $t('provider.dashboard.next_schedules.title') }}</div>
+    <div class="section-title font16 fontbold gradient-diarista q-mb-sm">{{ $t('provider.dashboard.next_schedules.title') }}</div>
 
     <div class="scroll-wrapper">
       <div class="scroll-track">
@@ -14,59 +14,60 @@
             <div class="row no-wrap items-center q-gutter-x-sm">
               <q-avatar size="48px">
                 <img v-if="item.customer_photo" :src="item.customer_photo" style="object-fit:cover" />
-                <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="font-size:14px;border-radius:50%">{{ (item.customer_name ?? item.client_name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
+                <span v-else class="full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="border-radius:50%">{{ (item.customer_name ?? item.client_name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
               </q-avatar>
               <div class="column flex-1">
                 <div class="row items-center q-gutter-x-xs">
-                  <span class="text-name ellipsis">{{ item.customer_name ?? item.client_name }}</span>
+                  <span class="text-name ellipsis font12 fontmedium">{{ item.customer_name ?? item.client_name }}</span>
                   <div v-if="item.customer_rating" class="row items-center">
                     <q-icon name="mdi-star" color="warning" size="14px" />
-                    <span class="text-rating text-text">{{ item.customer_rating }}</span>
+                    <span class="text-text font11">{{ item.customer_rating }}</span>
                   </div>
                 </div>
                 <div class="row items-center no-wrap">
-                  <span class="text-schedule-date-bold">{{ formatWeekday(item.date) }}</span>
-                  <span class="text-schedule-date-regular">{{ ', ' + formatDayMonth(item.date) }}</span>
+                  <span class="text-schedule-date-bold font9 fontbold">{{ formatWeekday(item.date) }}</span>
+                  <span class="text-schedule-date-regular font9">{{ ', ' + formatDayMonth(item.date) }}</span>
                 </div>
-                <div class="text-schedule-date-regular">
+                <div class="text-schedule-date-regular font9">
                   {{ $t('common.from') }}
-                  <span class="text-schedule-date-bold">{{ item.start_time?.slice(0, 5) }}</span>
+                  <span class="text-schedule-date-bold fontbold">{{ item.start_time?.slice(0, 5) }}</span>
                   {{ $t('common.to') }}
-                  <span class="text-schedule-date-bold">{{ item.end_time?.slice(0, 5) }}</span>
+                  <span class="text-schedule-date-bold fontbold">{{ item.end_time?.slice(0, 5) }}</span>
                 </div>
               </div>
               <div class="column items-end text-text">
-                <div class="text-price">{{ formatCurrency(item.total_amount) }}</div>
-                <div class="text-type">{{ t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
-                <div class="text-distance">{{ item.distance_km ?? 0 }}{{ $t('common.km') }}</div>
+                <div class="text-price font14 fontbold">{{ formatCurrency(item.total_amount) }}</div>
+                <div class="text-type font10 fontmedium">{{ t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
+                <div class="text-distance font10">{{ item.distance_km ?? 0 }}{{ $t('common.km') }}</div>
               </div>
             </div>
 
-            <div class="row q-mt-md items-center text-text text-caption q-px-xs">
+            <div class="row q-mt-md items-center text-text q-px-xs font9 fontmedium">
               <div class="col ellipsis text-grey-7">
                 {{ formatAddress(item.address) || 'N/A' }}
               </div>
-              <q-icon name="mdi-content-copy" color="primary" size="16px" class="q-ml-xs" @click="copyAddress(item.address)"/>
+              <q-icon name="mdi-content-copy" color="primary" size="12px" class="q-ml-xs" @click="copyAddress(item.address)"/>
             </div>
 
             <div class="row q-mt-sm items-center">
               <q-btn
-                flat
+                unelevated
                 no-caps
+                flat
                 color="primary"
-                size="sm"
-                class="col-auto q-px-none btn-details"
+                size="xs"
+                padding="4px 8px"
                 :label="$t('provider.dashboard.next_schedules.details')"
                 @click="emit('view-details', item)"
               />
               <q-space />
-              <div class="row items-center q-gutter-x-xs text-grey-7" style="font-size: 11px;">
-                <q-icon 
-                  :name="item.offers_meal ? 'mdi-silverware' : 'mdi-close-outline'" 
-                  :color="item.offers_meal ? 'secondary' : 'grey-5'" 
-                  size="14px" 
+              <div class="row items-center q-gutter-x-xs text-grey-7 font9">
+                <q-icon
+                  :name="item.offers_meal ? 'mdi-silverware' : 'mdi-close-outline'"
+                  :color="item.offers_meal ? 'secondary' : 'grey-5'"
+                  size="14px"
                 />
-                <span :class="item.offers_meal ? 'text-weight-medium' : ''">
+                <span :class="item.offers_meal ? 'fontmedium' : ''">
                   {{ item.offers_meal ? $t('provider.dashboard.next_schedules.offers_meal') : $t('provider.dashboard.next_schedules.no_meal') }}
                 </span>
               </div>
@@ -136,37 +137,19 @@ const copyAddress = (address) => {
   border-radius: 12px;
 }
 .text-name {
-  font-weight: 700;
   color: #3a3a4a;
 }
-.text-rating {
-  font-size: 12px;
-  font-weight: 600;
-}
 .text-schedule-date-bold {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 700;
   color: #3a3a4a;
 }
 .text-schedule-date-regular {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 400;
   color: #666;
 }
 .text-date, .text-time, .text-type, .text-region, .text-distance {
-  font-size: 11px;
   color: #666;
 }
 .text-price {
-  font-weight: 700;
   color: #3a3a4a;
-  font-size: 14px;
-}
-.btn-details {
-  font-weight: 700;
-  font-size: 13px;
 }
 .flex-1 {
   flex: 1;

+ 21 - 38
src/components/dashboard/DashboardOpportunities.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="q-mx-md q-mb-md">
     <div class="row items-center justify-between q-mb-sm">
-      <div class="section-title gradient-diarista">{{ $t('provider.dashboard.opportunities.title') }}</div>
+      <div class="section-title font16 fontbold gradient-diarista">{{ $t('provider.dashboard.opportunities.title') }}</div>
       <q-btn flat no-caps color="grey-6" size="sm" :label="$t('common.see_all')"  @click="openAllOpportunities"/>
     </div>
 
@@ -17,46 +17,49 @@
             <div class="row no-wrap items-center q-gutter-x-sm">
               <q-avatar size="48px">
                 <img v-if="item.customer_photo" :src="item.customer_photo" style="object-fit:cover" />
-                <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="font-size:14px;border-radius:50%">{{ (item.client_name ?? item.client?.user?.name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
+                <span v-else class="full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="border-radius:50%">{{ (item.client_name ?? item.client?.user?.name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
               </q-avatar>
               <div class="column flex-1">
                 <div class="row items-center q-gutter-x-xs">
-                  <span class="text-name ellipsis">{{ item.client?.user?.name }}</span>
+                  <span class="ellipsis font12 fontmedium text-text">{{ item.client?.user?.name }}</span>
                   <div class="row items-center">
-                    <q-icon name="mdi-star" color="warning" size="14px" />
-                    <span class="text-rating text-text">{{ item.client?.average_rating }}</span>
+                    <div class="q-my-auto">
+                      <q-icon name="mdi-star" color="warning" size="12px"/>                  
+                    </div>
+                    <div class="q-my-auto">
+                      <span class="text-text font9 fontregular ">{{ item.client?.average_rating }}</span>
+                    </div>
                   </div>
                 </div>
                 <div class="row items-center no-wrap">
-                  <span class="text-schedule-date-bold">{{ formatWeekday(item.date) }}</span>
-                  <span class="text-schedule-date-regular">{{ ', ' + formatDayMonth(item.date) }}</span>
+                  <span class="text-schedule-date-bold font9 fontbold">{{ formatWeekday(item.date) }}</span>
+                  <span class="text-schedule-date-regular font9">{{ ', ' + formatDayMonth(item.date) }}</span>
                 </div>
-                <div class="text-schedule-date-regular">
+                <div class="text-schedule-date-regular font9">
                   {{ $t('common.from') }}
-                  <span class="text-schedule-date-bold">{{ item.start_time?.slice(0, 5) }}</span>
+                  <span class="text-schedule-date-bold fontbold">{{ item.start_time?.slice(0, 5) }}</span>
                   {{ $t('common.to') }}
-                  <span class="text-schedule-date-bold">{{ item.end_time?.slice(0, 5) }}</span>
+                  <span class="text-schedule-date-bold fontbold">{{ item.end_time?.slice(0, 5) }}</span>
                 </div>
               </div>
               <div class="column items-end text-text">
-                <div class="text-price">{{ formatCurrency(item.total_amount) }}</div>
-                <div class="text-type">{{ $t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
-                <div class="text-region text-weight-bold">{{ item.address?.district || 'N/A' }}</div>
-                <div class="text-distance">{{ item.distance_km ?? 0 }}{{ $t('common.km') }}</div>
+                <div class="text-price font12 fontbold">{{ formatCurrency(item.total_amount) }}</div>
+                <div class="text-type font9">{{ $t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
+                <div class="text-region font9">{{ item.address?.district || 'N/A' }}</div>
+                <div class="text-distance font9">{{ item.distance_km ?? 0 }}{{ $t('common.km') }}</div>
               </div>
             </div>
 
             <div class="row q-mt-sm justify-between">
-              <div class="text-text text-primary">
-                  {{ item.custom_schedule?.service_type?.description }}
+              <div class="text-text text-primary font11 fontmedium">
+                  {{ item.custom_schedule?.address_type }}
               </div>
               <q-btn
                  unelevated
                  rounded
                  no-caps
                  color="primary"
-                 size="sm"
-                 class="btn-details"
+                 size="xs"
                  :label="$t('common.details')"
                  @click="openOpportunityDetails(item)"
                 />
@@ -141,37 +144,17 @@ const formatDayMonth = (iso) => {
   min-width: 85vw;
   border-radius: 12px;
 }
-.text-name {
-  font-weight: 700;
-  color: #3a3a4a;
-}
-.text-rating {
-  font-size: 12px;
-  font-weight: 600;
-}
 .text-schedule-date-bold {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 700;
   color: #3a3a4a;
 }
 .text-schedule-date-regular {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 400;
   color: #666;
 }
 .text-date, .text-time, .text-type, .text-region, .text-distance {
-  font-size: 11px;
   color: #666;
 }
 .text-price {
-  font-weight: 700;
   color: #3a3a4a;
-  font-size: 14px;
-}
-.btn-details {
-  font-weight: 700;
 }
 .flex-1 {
   flex: 1;

+ 8 - 19
src/components/dashboard/DashboardPriceSuggest.vue

@@ -4,24 +4,24 @@
       <q-card-section class="q-pa-md">
         <div class="row items-center justify-between q-mb-sm">
           <div class="row items-center q-gutter-x-sm">
-            <span class="text-suggest-label">{{ $t('provider.dashboard.price_suggest.region_label_1') }}</span>
-            <span class="text-suggest-label text-weight-bold">{{ $t('provider.dashboard.price_suggest.region_label_2') }}</span>
+            <span class="text-suggest-label font10">{{ $t('provider.dashboard.price_suggest.region_label_1') }}</span>
+            <span class="text-suggest-label font10 fontbold">{{ $t('provider.dashboard.price_suggest.region_label_2') }}</span>
           </div>
-          <q-badge rounded class="price-badge q-px-md q-py-xs gradient-diarista-bg">
+          <q-badge rounded class="price-badge font10 fontbold q-px-md q-py-xs gradient-diarista-bg">
             {{ formatCurrency(data?.average_price ?? 0) }}
           </q-badge>
         </div>
         <div class="row items-center justify-between no-wrap">
           <div class="row items-center q-gutter-x-sm">
-            <span class="text-suggest-label">{{ $t('provider.dashboard.price_suggest.my_price_label') }}</span>
+            <span class="text-suggest-label font10">{{ $t('provider.dashboard.price_suggest.my_price_label') }}</span>
             <q-chip class="row items-center no-wrap bg-surface" outline color="text">
-              <span class="text-my-price q-mr-xs">{{ showMyPrice ? formatCurrency(data?.your_price ?? 0) : $t('common.price_masked') }}</span>
+              <span class="text-my-price font10 fontsemibold q-mr-xs">{{ showMyPrice ? formatCurrency(data?.your_price ?? 0) : $t('common.price_masked') }}</span>
               <q-btn icon="mdi-eye-off-outline" flat size="xs" color="grey-6" class="q-pa-none q-pl-sm" @click="showMyPrice = !showMyPrice"/>
             </q-chip>
           </div>
-          <q-btn flat no-caps color="primary" padding="0" class="btn-alter" @click="openServiceDataDialog">
-            <div class="row items-center q-gutter-x-xs">
-              <span class="text-weight-bold">{{ $t('common.alter') }}</span>
+          <q-btn flat no-caps color="primary" size="xs" padding="0" @click="openServiceDataDialog">
+            <div class="row items-center q-gutter-x-xs font10">
+              <span class="fontbold">{{ $t('common.alter') }}</span>
               <q-icon name="mdi-pencil-outline" size="xs" />
             </div>
           </q-btn>
@@ -64,23 +64,12 @@ const openServiceDataDialog = () => {
   border-radius: 12px;
 }
 .text-suggest-label {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
   color: #3a3a4a;
 }
 .price-badge {
-  font-family: "Inter", sans-serif;
-  font-size: 14px;
-  font-weight: 700;
   border-radius: 20px;
 }
 .text-my-price {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 600;
   color: #3a3a4a;
 }
-.btn-alter {
-  font-size: 11px;
-}
 </style>

+ 8 - 1
src/components/dashboard/DashboardScrollAreaSchedules.vue

@@ -17,15 +17,22 @@ import Banner1 from 'src/assets/banner_1.svg';
 import Banner2 from 'src/assets/banner_2.svg';
 
 import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import ProfileHelpDialog from 'src/components/profile/ProfileHelpDialog.vue';
 
 const router = useRouter();
+const $q = useQuasar();
 
 const cards = [
   { id: 1, image: Banner1, alt: 'Diária sob medida', routeName: 'OpportunitiesPage' },
-  { id: 2, image: Banner2, alt: 'Escolha profissionais' },
+  { id: 2, image: Banner2, alt: 'Como aceitar uma diária', action: 'help' },
 ];
 
 const goToCard = (card) => {
+  if (card.action === 'help') {
+    $q.dialog({ component: ProfileHelpDialog })
+    return
+  }
   if (card.routeName) {
     router.push({ name: card.routeName })
   }

+ 20 - 56
src/components/dashboard/DashboardSolicitations.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="q-mx-md q-mb-md">
     <div class="row items-center justify-between q-mb-sm">
-      <div class="section-title gradient-diarista ">{{ $t('provider.dashboard.solicitations.title') }}</div>
+      <div class="section-title font16 fontbold gradient-diarista">{{ $t('provider.dashboard.solicitations.title') }}</div>
       <q-btn flat no-caps color="grey-6" icon="mdi-chevron-right-outlined" size="sm" :label="$t('common.see_all')" />
     </div>
 
@@ -14,39 +14,37 @@
           :flat="false"
         >
           <q-card-section class="q-pa-sm">
-            <div class="row no-wrap items-center q-gutter-x-sm">
+            <div class="row no-wrap items-center q-gutter-x-sm text-text">
               <q-avatar size="48px">
                 <img v-if="item.customer_photo" :src="item.customer_photo" style="object-fit:cover" />
-                <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="font-size:14px;border-radius:50%">{{ item.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
+                <span v-else class="full-width full-height flex flex-center" :style="avatarColors[item.id % avatarColors.length]" style="border-radius:50%">{{ item.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
               </q-avatar>
               <div class="column flex-1">
                 <div class="row items-center q-gutter-x-xs">
-                  <span class="text-name ellipsis">{{ item.client_name }}</span>
+                  <span class="ellipsis font9 fontmedium">{{ item.client_name }}</span>
                   <div class="row items-center">
-                    <q-icon name="mdi-star" color="warning" size="14px" />
-                    <span class="text-rating text-text">{{ item.average_rating }}</span>
+                    <q-icon name="mdi-star" color="warning" size="12px" />
+                    <span class="text-text font9">{{ item.average_rating }}</span>
                   </div>
                 </div>
-                <div class="row items-center no-wrap">
-                  <span class="text-schedule-date-bold">{{ formatWeekday(item.date) }}</span>
-                  <span class="text-schedule-date-regular">{{ ', ' + formatDayMonth(item.date) }}</span>
+                <div class="row items-center no-wrap font9">
+                  <span class="fontbold">{{ formatWeekday(item.date) }}</span>
+                  <span class="">{{ ', ' + formatDayMonth(item.date) }}</span>
                 </div>
-                <div class="text-schedule-date-regular">
+                <div class="font9">
                   {{ $t('common.from') }}
-                  <span class="text-schedule-date-bold">
+                  <span class="fontbold">
                     {{ item.start_time?.slice(0, 5) }}
-
                     {{ $t('common.to') }}
-
                     {{ item.end_time?.slice(0, 5) }}
                   </span>
                 </div>
               </div>
               <div class="column items-end text-text">
-                <div class="text-price">{{ formatCurrency(item.total_amount) }}</div>
-                <div class="text-type">{{ t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
-                <div class="text-region text-weight-bold">{{ item.address?.district || 'N/A' }}</div>
-                <div class="text-distance">
+                <div class="font12 fontbold">{{ formatCurrency(item.total_amount) }}</div>
+                <div class="font9">{{ t(labelsPeriodTypes.find(label => label.value == item.period_type)?.label) }}</div>
+                <div class="font9 fontbold">{{ item.address?.district || 'N/A' }}</div>
+                <div class="font9">
                   <span class="q-pr-xs">{{ item.distance_km ?? 0 }}</span>
                   {{ $t('common.km') }}
                 </div>
@@ -55,18 +53,18 @@
 
             <div class="row q-mt-sm q-gutter-x-sm items-center">
               <div>
+                <div class="text-text font9 fontmedium">
+                  {{ item.time_since_request }}
+                </div>
                 <q-btn
                   flat
                   no-caps
                   color="primary"
-                  size="sm"
-                  class="col-auto q-px-none btn-details"
+                  size="xs"
+                  class="col-auto q-px-none"
                   :label="$t('common.details')"
                   @click="emit('view-details', item)"
                 />
-                <div class="text-schedule-date-bold text-text text-weight-bold">
-                  {{ item.time_since_request }}
-                </div>
               </div>
               <q-space />
               <q-btn
@@ -150,41 +148,7 @@ const formatDayMonth = (iso) => {
 .solicitation-card {
   min-width: 85vw;
 }
-.text-name {
-  font-weight: 700;
-  color: #3a3a4a;
-}
-.text-rating {
-  font-size: 12px;
-  font-weight: 600;
-}
-.text-schedule-date-bold {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 700;
-  color: #3a3a4a;
-}
-.text-schedule-date-regular {
-  font-family: "Inter", sans-serif;
-  font-size: 11px;
-  font-weight: 400;
-  color: #666;
-}
-.text-date, .text-time, .text-type, .text-region, .text-distance {
-  font-size: 11px;
-  color: #666;
-}
-.text-price {
-  font-weight: 700;
-  color: #3a3a4a;
-  font-size: 14px;
-}
-.btn-details {
-  font-weight: 700;
-  font-size: 13px;
-}
 .btn-action {
-  font-weight: 700;
   min-width: 90px;
 }
 .flex-1 {

+ 7 - 10
src/components/dashboard/DashboardSummaryInfos.vue

@@ -3,26 +3,26 @@
     <q-card-section class="q-pa-md">
       <div class="row items-center no-wrap q-gutter-x-md">
         <div class="row items-center no-wrap q-gutter-x-sm col">
-          <q-avatar size="54px" :style="!data?.profile_photo ? avatarStyle : undefined" class="text-weight-bold text-h6">
+          <q-avatar size="54px" :style="!data?.profile_photo ? avatarStyle : undefined" class="">
             <img v-if="data?.profile_photo" :src="data.profile_photo" style="object-fit:cover;width:100%;height:100%;border-radius:50%" />
             <span v-else>{{ data?.name?.slice(0, 2).toUpperCase() ?? '??' }}</span>
           </q-avatar>
           <div class="column q-gutter-y-xs min-width-0">
-            <span class="summary-greeting text-greeting">{{ $t('provider.dashboard.summary.welcome') }}</span>
-            <span class="summary-name text-name text-primary">{{ data?.name ?? $t('provider.dashboard.summary.welcome') }}</span>
+            <span class="summary-greeting font14 fontmedium">{{ $t('provider.dashboard.summary.welcome') }}</span>
+            <span class="summary-name text-primary font16 fontbold">{{ data?.name ?? $t('provider.dashboard.summary.welcome') }}</span>
           </div>
         </div>
         <div class="column items-end q-gutter-y-xs col-auto">
-          <span class="summary-label text-label-bold text-text">{{ $t('provider.dashboard.summary.my_schedules') }}</span>
+          <span class="summary-label text-grey-6 font12 fontbold">{{ $t('provider.dashboard.summary.my_schedules') }}</span>
           <span class="summary-count row">
-            <q-icon name="mdi-clock-check-outline" class="q-my-auto" size="xs" color="grey-6" />
-            <span class="q-my-auto q-ml-sm text-caption text-weight-bold">{{ data?.pending_services ?? 0 }}</span>
+            <q-icon name="mdi-clock-check-outline" class="q-my-auto" size="xs" color="positive" />
+            <span class="q-my-auto q-ml-sm">{{ data?.pending_services ?? 0 }}</span>
           </span>
         </div>
       </div>
 
       <div class="row items-center justify-between no-wrap q-mt-xs">
-        <div class="text-text col text-caption">
+        <div class="text-text col font8 fontmedium ellipsis">
           {{ formatAddress(data.address) }}
         </div>
         <q-icon name="mdi-chevron-down" color="secondary" size="18px" class="col-auto" />
@@ -49,9 +49,6 @@ const avatarStyle = {
 .summary-name { display: block; line-height: 1.1; }
 .summary-label { white-space: nowrap; }
 .summary-count {
-  font-family: "Inter", sans-serif;
-  font-size: 20px;
-  font-weight: 500;
   color: #3a3a4a;
 }
 </style>

+ 62 - 70
src/components/dashboard/DashboardTodayServices.vue

@@ -12,51 +12,50 @@
             <div class="row no-wrap items-start q-mb-xs">
               <div class="col-8 row">
                 <q-avatar size="40px" class="flex-shrink-0 q-mr-sm">
+                  <img v-if="item.customer_photo" :src="item.customer_photo" style="object-fit:cover" />
                   <span
+                    v-else
                     :style="avatarColors[item.id % avatarColors.length]"
-                    class="text-weight-bold full-width full-height flex flex-center"
-                    style="font-size:14px; border-radius:50%;"
+                    class="full-width full-height flex flex-center"
+                    style="border-radius:50%;"
                   >
                     {{ item.client_name?.slice(0, 2).toUpperCase() ?? '??' }}
                   </span>
                 </q-avatar>
 
                 <div class="col column no-wrap overflow-hidden justify-center">
-                  <span class="text-caption text-text">
+                  <span class="text-text font12">
                     <template v-if="cardState(item) === 'finished'">
                       {{ $t('provider.dashboard.today_services.finished_label') }}
-                      <span class="text-weight-bold"> {{ ' ' + item.client_name ?? '—' }}</span>
+                      <span class="fontbold"> {{ ' ' + item.client_name ?? '—' }}</span>
                       {{ ' ' + $t('provider.dashboard.today_services.finished_suffix') }}
                     </template>
                     <template v-else>
                       {{ $t('provider.dashboard.today_services.start_label') }}
-                      <span class="text-weight-bold"> {{ ' ' + item.client_name ?? '—' }}</span>
+                      <span class="fontbold"> {{ ' ' + item.client_name ?? '—' }}</span>
                     </template>
                   </span>
                   <div class="row items-center q-mt-xs no-wrap">
                     <q-icon name="mdi-clock-outline" size="12px" class="q-mr-xs gradient-diarista" />
-                    <span class="hours-font text-grey-5">
+                    <span class="text-grey-5 font9 fontregular">
                       {{ $t('common.from') }} {{  item.start_time?.slice(0, 5) }} {{ $t('common.to') }} {{ item.end_time?.slice(0, 5) }}
                     </span>
                   </div>
                 </div>
               </div>
 
-              <div class="flex-shrink-0 row items-center justify-center col-4">
+              <div class="flex-shrink-0 row items-right justify-end col-4">
                 <div v-if="cardState(item) === 'awaiting_code'" class="col-12 row items-center justify-center q-pb-sm q-px-sm">
-                  <div class="hint-text text-caption text-text text-center q-mb-xs col-12">
+                  <div class="hint-text text-text text-center font9 q-mb-xs col-12">
                     {{ $t('provider.dashboard.today_services.code_hint') }}
                   </div>
-                  <!-- <div class="hint-text text-caption text-text text-center q-mb-xs col-12">
-                    {{ $t('provider.dashboard.today_services.code_hint2') }}
-                  </div> -->
                   <div class="code-input-row col-12">
                     <input
                       v-for="(_, idx) in 4"
                       :key="idx"
                       :ref="el => setCodeRef(item.id, idx, el)"
                       v-model="codeInputs[item.id][idx]"
-                      class="code-input-box"
+                      class="code-input-box font9 fontbold"
                       type="tel"
                       maxlength="1"
                       inputmode="numeric"
@@ -65,17 +64,17 @@
                       @paste.prevent="onCodePaste(item, $event)"
                     />
                   </div>
-                  <div v-if="codeError[item.id]" class="text-negative code-error-text q-mt-xs text-center">
+                  <div v-if="codeError[item.id]" class="text-negative code-error-text font9 q-mt-xs text-center">
                     {{ codeError[item.id] }}
                   </div>
                 </div>
 
                 <div v-else-if="cardState(item) === 'in_progress'" class="col-12 text-center q-mb-xs">
                   <div class="column items-center">
-                    <span class="badge-status-text text-text text-weight-bold q-my-xs">
+                    <span class="text-text font9 fontmedium q-my-xs">
                       {{ $t('provider.dashboard.today_services.in_progress') }}
                     </span>
-                    <div class="code-pill bg-positive row">
+                    <div class="code-pill font12 fontbold bg-positive row">
                       <q-icon
                         name="mdi-check" size="12px" class="q-mr-xs q-my-auto"
                       />
@@ -84,22 +83,25 @@
                   </div>
                 </div>
 
-                <div v-else>
-                  <div v-if="item.provider_reviewed" class="rate-btn reviewed-badge">
-                    <q-icon name="mdi-star" size="14px" class="q-mr-xs" />
+                <div v-else class="">
+                  <div v-if="item.provider_reviewed" class="rate-btn reviewed-badge column  fontsemibold">
+                    <div class="text-center">
+                      <q-icon name="mdi-star" size="14px" class="q-mr-xs " />
+                    </div>
                     {{ $t('provider.dashboard.today_services.reviewed_badge') }}
                   </div>
-                  <q-btn
-                    v-else
-                    unelevated no-caps
-                    class="rate-btn"
-                    @click.stop="emit('rate', item)"
-                  >
-                    <div class="column items-center">
-                      <q-icon name="mdi-star-outline" size="12px" class="q-mr-xs" />
-                      {{ $t('provider.dashboard.today_services.rate_btn') }}
-                    </div>
-                  </q-btn>
+                  <div v-else class="">
+                    <q-btn
+                      unelevated no-caps
+                      class="rate-btn font10 fontmedium"
+                      @click.stop="emit('rate', item)"
+                    >
+                      <div class="column items-center">
+                        <q-icon name="mdi-star-outline" size="14px" class="q-mr-xs" />
+                        {{ $t('provider.dashboard.today_services.rate_btn') }}
+                      </div>
+                    </q-btn>
+                  </div>
                 </div>
               </div>
             </div>
@@ -124,16 +126,17 @@
               <q-space />
               <template v-if="cardState(item) === 'in_progress'">
                 <q-icon name="mdi-clock-outline" size="13px" class="q-mr-xs flex-shrink-0 gradient-diarista" />
-                <span class="text-caption text-grey-6 text-right text-no-wrap">
+                <span class="text-grey-6 text-right text-no-wrap font11">
                   {{ $t('provider.dashboard.today_services.end_time_label') }} 
                   <span class="gradient-diarista">{{ calculateTimeUntilEnd(item.end_time) }}</span>
                 </span>
               </template>
               <template v-else-if="cardState(item) === 'awaiting_code'">
                 <q-icon name="mdi-map-marker-outline" size="13px" color="grey-6" class="q-mr-xs flex-shrink-0" />
-                <span class="text-caption text-grey-6 col ellipsis text-right">
+                <span class="text-grey-6 col ellipsis text-right font9 fontmedium">
                   {{ [item.address?.address, item.address?.number, item.address?.district].filter(Boolean).join(', ') || '—' }}
                 </span>
+                <q-icon name="mdi-content-copy" color="primary" size="16px" class="q-ml-xs" @click="copyAddress(item.address)"/>
               </template>
             </div>
           </q-card-section>
@@ -149,6 +152,7 @@ import { useQuasar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 import ProfileHelpDialog from 'src/components/profile/ProfileHelpDialog.vue'
 import { verifyScheduleCode } from 'src/api/schedule'
+import { formatAddress } from 'src/helpers/utils';
 
 const props = defineProps({ data: { type: Array, default: () => [] } })
 const emit = defineEmits(['rate', 'refresh'])
@@ -262,6 +266,14 @@ const calculateTimeUntilEnd = (endTimeStr) => {
 const openHelp = () => {
   $q.dialog({ component: ProfileHelpDialog })
 }
+
+const copyAddress = (address) => {
+  const formatted = formatAddress(address);
+  if (formatted) {
+    navigator.clipboard.writeText(formatted);
+  }
+  $q.notify({ message: t('provider.dashboard.next_schedules.address_copied'), color: 'positive' });
+};
 </script>
 
 <style scoped lang="scss">
@@ -287,14 +299,14 @@ const openHelp = () => {
 .hint-text {
   max-width: 100px;
   line-height: 1.3;
-  font-size: 9px;
 }
 
 .code-input-row { 
   display: flex;
   align-items: center; 
   justify-content: center; 
-  gap: 4px; background: #d1d5db; 
+  gap: 4px;
+  background: #d1d5db; 
   border-radius: 999px; 
   padding: 4px 8px; 
   width: 110px; 
@@ -302,66 +314,49 @@ const openHelp = () => {
   margin: 0 auto; 
 }
 
-.code-input-box { 
-  width: 16px; 
-  height: 18px; 
-  background: transparent; 
-  border: none; 
-  border-bottom: 2px solid white; 
-  border-radius: 0; 
-  text-align: center; 
-  font-size: 11px; 
-  font-weight: 700; 
-  color: #1a1a2e; 
-  outline: none; 
-  caret-color: transparent; 
-  &:focus { 
-    border-bottom: 2px solid #ffffff; 
-    box-shadow: none; 
-  } 
+.code-input-box {
+  width: 12px;
+  height: 18px;
+  background: transparent;
+  border: none;
+  border-bottom: 1px solid white;
+  border-radius: 0;
+  text-align: center;
+  color: #1a1a2e;
+  outline: none;
+  caret-color: transparent;
+  &:focus {
+    border-bottom: 2px solid #ffffff;
+    box-shadow: none;
+  }
 }
 
 .code-error-text {
-  font-size: 9px;
   max-width: 110px;
   line-height: 1.2;
 }
 
 .code-pill {
   color: white;
-  font-weight: 800;
-  font-size: 12px;
   letter-spacing: 2px;
   border-radius: 20px;
   padding: 1px 8px;
 }
 
-.badge-status-text {
-  font-size: 11px;
-  
-}
-
 .rate-btn {
   background: #EC4899;
   color: white;
-  font-weight: 600;
-  font-size: 11px;
   white-space: nowrap;
   border-radius: 10px !important;
-  min-width: 45px;
-  min-height: 40px;
+  min-width: 55px;
+  min-height: 50px;
   padding: 3px 5px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
   gap: 4px;
 }
 
 .reviewed-badge {
   cursor: default;
   opacity: 0.85;
-  font-size: 11px;
 }
 
 .progress-track {
@@ -383,7 +378,4 @@ const openHelp = () => {
   }
 }
 
-.hours-font {
-  font-size: 11px;
-}
 </style>

+ 26 - 24
src/components/dashboard/NextSchedulesDetailsDialog.vue

@@ -9,27 +9,27 @@
       <q-card-section class="column items-center q-pt-xs q-pb-sm">
         <q-avatar size="72px" class="q-mb-sm">
           <img v-if="schedule.customer_photo" :src="schedule.customer_photo" style="object-fit:cover" />
-          <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[schedule.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ (schedule.customer_name ?? schedule.client_name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
+          <span v-else class="full-width full-height flex flex-center" :style="avatarColors[schedule.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ (schedule.customer_name ?? schedule.client_name)?.slice(0,2).toUpperCase() ?? '??' }}</span>
         </q-avatar>
-        <div class="text-subtitle1 text-weight-bold text-text">
+        <div class="text-text font16 fontbold">
           {{ schedule.customer_name ?? schedule.client_name }}
         </div>
-        <div class="text-price q-mt-xs">{{ formatCurrency(schedule.total_amount) }}</div>
-        <div class="text-caption text-grey-6 q-mt-xxs">{{ periodLabel }}</div>
+        <div class="text-price font16 fontbold q-mt-xs">{{ formatCurrency(schedule.total_amount) }}</div>
+        <div class="text-grey-6 font12 q-mt-xxs">{{ periodLabel }}</div>
       </q-card-section>
 
       <q-separator class="q-mx-lg" />
 
-      <q-card-section class="column items-center q-py-sm text-text">
-        <div class="text-body2 text-center">
-          <span class="text-weight-bold">{{ weekdayLabel }}</span>
+      <q-card-section class="column items-center q-py-sm text-text font12">
+        <div class="text-center">
+          <span class="fontbold">{{ weekdayLabel }}</span>
           <span class="text-grey-7">{{', ' + dayMonthLabel }}</span>
         </div>
-        <div class="text-body2 text-center q-mt-xxs">
+        <div class="text-center q-mt-xxs">
           {{ $t('common.from') }}
-          <span class="text-weight-bold">{{ schedule.start_time?.slice(0, 5) }}</span>
+          <span class="fontbold">{{ schedule.start_time?.slice(0, 5) }}</span>
           {{ $t('common.to') }}
-          <span class="text-weight-bold">{{ schedule.end_time?.slice(0, 5) }}</span>
+          <span class="fontbold">{{ schedule.end_time?.slice(0, 5) }}</span>
         </div>
       </q-card-section>
 
@@ -42,7 +42,7 @@
             :color="schedule.offers_meal ? 'secondary' : 'grey-5'"
             size="18px"
           />
-          <span class="text-body2" :class="schedule.offers_meal ? 'text-text' : 'text-grey-6'">
+          <span class="font12" :class="schedule.offers_meal ? 'text-text' : 'text-grey-6'">
             {{ schedule.offers_meal
               ? $t('provider.dashboard.next_schedules.offers_meal')
               : $t('provider.dashboard.next_schedules.no_meal') }}
@@ -54,17 +54,27 @@
 
       <q-card-section class="column items-center q-py-sm q-px-lg">
         <div class="row items-start q-gutter-x-xs no-wrap">
-          <q-icon name="mdi-map-marker-outline" color="primary" size="20px" class="q-mt-xxs flex-shrink-0" />
-          <span class="text-body2 text-grey-8 text-center">{{ formattedAddress || 'N/A' }}</span>
+          <div class="col-1 q-my-auto">
+            <q-icon name="mdi-map-marker-outline" color="primary" size="20px" class="q-mt-xxs flex-shrink-0" />
+          </div>
+          <div class="col">
+            <span class="text-grey-8 text-center font12">{{ formattedAddress || 'N/A' }}</span>
+          </div>
+        </div>
+        <div class="text-grey-6 text-center font12 q-mt-xs">
+          {{ $t('provider.dashboard.solicitations.distance_prefix') }}
+          <strong>{{ (schedule.distance_km ?? 0) + ' km' }}</strong>
+          {{ $t('provider.dashboard.solicitations.distance_suffix') }}
         </div>
         <q-btn
           flat
           no-caps
           dense
           color="primary"
+          size="sm"
           icon="mdi-content-copy"
           :label="$t('provider.dashboard.next_schedules.copy_address')"
-          class="q-mt-xs btn-copy"
+          class="q-mt-xs"
           @click="copyAddress"
         />
       </q-card-section>
@@ -75,7 +85,8 @@
           rounded
           no-caps
           color="primary"
-          class="full-width btn-close"
+          size="sm"
+          class="full-width btn-close font14 fontbold"
           :label="$t('provider.dashboard.cancel_schedule.close')"
           @click="onDialogCancel"
         />
@@ -192,22 +203,13 @@ const openHelp = () => {
 }
 
 .text-price {
-  font-size: 26px;
-  font-weight: 700;
   color: var(--q-primary);
 }
 
 .btn-close {
-  font-weight: 700;
-  font-size: 16px;
   padding: 10px 0;
 }
 
-.btn-copy {
-  font-size: 12px;
-  font-weight: 600;
-}
-
 .flex-shrink-0 {
   flex-shrink: 0;
 }

+ 8 - 15
src/components/dashboard/ScheduleCancelDialog.vue

@@ -7,13 +7,13 @@
       </div>
 
       <q-card-section class="q-pt-none q-pb-sm q-px-lg text-center">
-        <div class="cancel-title text-secondary text-weight-bold">
+        <div class="cancel-title text-primary font16 fontbold">
           {{ $t('provider.dashboard.cancel_schedule.title') }}
         </div>
       </q-card-section>
 
       <q-card-section class="q-pt-none q-pb-sm q-px-lg">
-        <div class="text-body2 text-grey-8 text-weight-bold q-mb-xs">
+        <div class="text-grey-8 font14 fontbold q-mb-xs">
           {{ $t('provider.dashboard.cancel_schedule.reason_label') }}
         </div>
         <q-input
@@ -32,17 +32,10 @@
       <q-card-section class="q-pt-xs q-pb-md q-px-lg">
         <div class="warning-box row no-wrap q-gutter-x-sm q-pa-sm">
           <q-icon name="mdi-alert-outline" color="secondary" size="22px" class="q-mt-xs flex-shrink-0" />
-          <div>
-            <span class="text-caption text-weight-bold text-grey-9">
-              {{ $t('provider.dashboard.cancel_schedule.warning_title') }}
-            </span>
-            <span class="text-caption text-grey-8">
-              {{ ' ' + $t('provider.dashboard.cancel_schedule.warning_free') }}
-            </span>
-            <br />
-            <span class="text-caption text-grey-8">
-              {{ $t('provider.dashboard.cancel_schedule.warning_fee') }}
-            </span>
+          <div class="font12 text-grey-8">
+            <span>{{ $t('provider.dashboard.cancel_schedule.warning_before') }}</span>
+            <span class="fontbold">{{ $t('provider.dashboard.cancel_schedule.warning_bold') }}</span>
+            <span>{{ $t('provider.dashboard.cancel_schedule.warning_after') }}</span>
           </div>
         </div>
       </q-card-section>
@@ -53,6 +46,7 @@
             unelevated
             rounded
             no-caps
+            size="sm"
             class="btn-action bg-grey-3 text-grey-8"
             :label="$t('provider.dashboard.cancel_schedule.btn_cancel')"
             @click="onDialogCancel"
@@ -64,6 +58,7 @@
             color="secondary"
             class="btn-action"
             :loading="loading"
+            size="sm"
             :disable="!cancelText || cancelText.trim().length < 5"
             :label="$t('provider.dashboard.cancel_schedule.btn_keep')"
             @click="confirmCancel"
@@ -118,7 +113,6 @@ const confirmCancel = async () => {
 }
 
 .cancel-title {
-  font-size: 18px;
   line-height: 1.35;
 }
 
@@ -129,7 +123,6 @@ const confirmCancel = async () => {
 
 .btn-action {
   min-width: 110px;
-  font-weight: 700;
 }
 
 .flex-shrink-0 {

+ 10 - 15
src/components/dashboard/ScheduleRatingDialog.vue

@@ -10,13 +10,13 @@
         <q-avatar size="64px" class="q-mb-sm">
           <span
             :style="avatarStyle"
-            class="text-weight-bold full-width full-height flex flex-center"
+            class="full-width full-height flex flex-center"
             style="font-size: 20px; border-radius: 50%;"
           >
             {{ initials }}
           </span>
         </q-avatar>
-        <div class="text-body1 text-text text-weight-bold text-center q-px-lg" style="line-height:1.3">
+        <div class="text-text text-center font16 fontbold q-px-lg" style="line-height:1.3">
           {{ $t('provider.dashboard.schedule_rating.title') }}
           <span class="text-primary"> {{ schedule.client_name + '?' }}</span>
         </div>
@@ -35,7 +35,7 @@
       </div>
 
       <q-card-section v-if="stars > 0" class="q-pt-xs q-pb-xs">
-        <div class="text-caption text-grey-7 text-center q-mb-sm">
+        <div class="text-grey-7 text-center font12 q-mb-sm">
           {{ isNegative ? $t('provider.dashboard.schedule_rating.negative_label') : $t('provider.dashboard.schedule_rating.positive_label') }}
         </div>
         <div v-if="loadingTags" class="row justify-center q-py-sm">
@@ -45,8 +45,8 @@
           <div
             v-for="tag in tags"
             :key="tag.id"
-            class="tag-pill"
-            :class="{ 'tag-pill--selected': selectedTagIds.includes(tag.id) }"
+            class="tag-pill font12"
+            :class="{ 'tag-pill--selected': selectedTagIds.includes(tag.id), 'fontsemibold': selectedTagIds.includes(tag.id) }"
             @click="toggleTag(tag.id)"
           >
             {{ tag.description }}
@@ -55,7 +55,7 @@
       </q-card-section>
 
       <q-card-section class="q-pt-xs q-pb-xs q-px-lg">
-        <div class="text-caption text-grey-7 q-mb-xs">
+        <div class="text-grey-7 font12 q-mb-xs">
           {{ $t('provider.dashboard.schedule_rating.comment_placeholder') }}
         </div>
         <q-input
@@ -86,7 +86,7 @@
           </div>
           <div v-if="photos.length < 5" class="photo-add" @click="photoInputRef.click()">
             <q-icon name="mdi-camera-plus-outline" size="24px" color="grey-5" />
-            <div class="photo-add__label">{{ $t('provider.dashboard.schedule_rating.add_photo') }}</div>
+            <div class="photo-add__label font9">{{ $t('provider.dashboard.schedule_rating.add_photo') }}</div>
           </div>
         </div>
       </q-card-section>
@@ -110,7 +110,7 @@
           no-caps
           full-width
           color="primary"
-          class="submit-btn col-12"
+          class="submit-btn font16 fontbold col-12"
           :label="$t('provider.dashboard.schedule_rating.submit_btn')"
           :loading="loading"
           :disable="stars === 0"
@@ -119,7 +119,7 @@
       </q-card-section>
 
       <q-card-section class="q-pt-xs q-pb-lg text-center">
-        <span class="text-caption text-grey-6 cursor-pointer" @click="openHelp">
+        <span class="text-grey-6 font12 cursor-pointer" @click="openHelp">
           {{ $t('provider.dashboard.schedule_rating.help_link') }}
         </span>
       </q-card-section>
@@ -216,7 +216,7 @@ const submit = async () => {
     await createReview({
       schedule_id: props.schedule.id,
       origin: 'provider',
-      origin_id: store.user.provider.id,
+      origin_id: store.user.provider_id,
       stars: stars.value,
       comment: comment.value || null,
       improvements_ids: selectedTagIds.value,
@@ -264,7 +264,6 @@ onMounted(async () => {
   border: 1.5px solid #d1d5db;
   border-radius: 20px;
   padding: 5px 14px;
-  font-size: 12px;
   color: #6b7280;
   cursor: pointer;
   transition: border-color 0.15s, color 0.15s;
@@ -273,13 +272,10 @@ onMounted(async () => {
   &--selected {
     border-color: #8B5CF6;
     color: #8B5CF6;
-    font-weight: 600;
   }
 }
 
 .submit-btn {
-  font-size: 15px;
-  font-weight: 700;
   padding: 10px 0;
 }
 
@@ -320,7 +316,6 @@ onMounted(async () => {
   flex-shrink: 0;
 
   &__label {
-    font-size: 9px;
     color: #9ca3af;
     text-align: center;
     margin-top: 2px;

+ 38 - 56
src/components/dashboard/SolicitationDetailsDialog.vue

@@ -12,15 +12,15 @@
         <q-card-section class="column items-center q-pt-xs q-pb-xs">
           <q-avatar size="72px" class="q-mb-sm">
             <img v-if="solicitation.customer_photo" :src="solicitation.customer_photo" style="object-fit:cover" />
-            <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[solicitation.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ solicitation.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
+            <span v-else class="full-width full-height flex flex-center" :style="avatarColors[solicitation.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ solicitation.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
           </q-avatar>
-          <div class="text-subtitle1 text-weight-bold text-text">
+          <div class="text-text font16 fontbold">
             {{ solicitation.client_name }}
           </div>
-          <div v-if="solicitation.address?.district" class="text-caption text-grey-6 q-mt-xs">
+          <div v-if="solicitation.address?.district" class="text-grey-6 font12 q-mt-xs">
             {{ solicitation.address.district }}
           </div>
-          <div v-if="solicitation.address" class="text-caption text-grey-5 text-center q-mt-xs">
+          <div v-if="solicitation.address" class="text-grey-6 font12 text-center q-mt-xs">
             {{ formatAddressShort(solicitation.address) }}
           </div>
         </q-card-section>
@@ -38,7 +38,7 @@
               :color="service.active ? 'secondary' : 'grey-4'"
               size="16px"
             />
-            <span class="text-body2 text-grey-8">{{ service.label }}</span>
+            <span class="text-grey-8 font14">{{ service.label }}</span>
           </div>
         </q-card-section>
 
@@ -46,30 +46,30 @@
 
         <q-card-section class="q-pt-xs q-pb-xs q-px-lg">
           <div class="detail-row">
-            <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.date') }}</span>
-            <span class="detail-value text-grey-9">{{ fullDateLabel }}</span>
+            <span class="text-secondary font12 fontsemibold">{{ $t('common.terms.date') }}</span>
+            <span class="text-grey-9 font13">{{ fullDateLabel }}</span>
           </div>
           <div class="detail-row">
-            <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.hour') }}</span>
-            <span class="detail-value text-grey-9">
+            <span class="text-secondary font12 fontsemibold">{{ $t('common.terms.hour') }}</span>
+            <span class="text-grey-9 font13">
               {{ solicitation.start_time?.slice(0, 5) }} {{ $t('common.to') }} {{ solicitation.end_time?.slice(0, 5) }}
             </span>
           </div>
           <div class="detail-row">
-            <span class="detail-label text-secondary text-weight-bold">{{ $t('common.terms.total_amount') }}</span>
-            <span class="detail-value text-grey-9">{{ formatCurrency(baseAmount) }}</span>
+            <span class="text-secondary font12 fontsemibold">{{ $t('common.terms.total_amount') }}</span>
+            <span class="text-grey-9 font13">{{ formatCurrency(baseAmount) }}</span>
           </div>
           <div class="detail-row">
-            <span class="detail-label text-secondary text-weight-bold">{{ $t('provider.dashboard.cancel_schedule.service_fee') }}</span>
-            <span class="detail-value text-grey-9">{{ formatCurrency(serviceFee) }}</span>
+            <span class="text-secondary font12 fontsemibold">{{ $t('provider.dashboard.cancel_schedule.service_fee') }}</span>
+            <span class="text-grey-9 font13">{{ formatCurrency(serviceFee) }}</span>
           </div>
         </q-card-section>
 
         <q-separator class="q-mx-md q-my-xs" />
 
         <q-card-section class="q-pt-xs q-pb-sm text-center">
-          <span class="text-weight-bold text-grey-9">{{ $t('provider.dashboard.cancel_schedule.total') }}</span>
-          <span class="total-value text-secondary text-weight-bold q-ml-xs">{{ formatCurrency(solicitation.total_amount) }}</span>
+          <span class="text-grey-9 fontbold">{{ $t('provider.dashboard.cancel_schedule.total') }}</span>
+          <span class="text-secondary fontbold font22 q-ml-xs">{{ formatCurrency(solicitation.total_amount) }}</span>
         </q-card-section>
 
         <q-card-section class="q-pt-none q-pb-xs q-px-lg">
@@ -78,7 +78,7 @@
             rounded
             no-caps
             color="secondary"
-            class="full-width btn-close-paid"
+            class="full-width btn-close-paid font16 fontbold"
             :label="$t('provider.dashboard.cancel_schedule.close')"
             @click="onDialogCancel"
           />
@@ -112,20 +112,20 @@
         <q-card-section class="column items-center q-pt-sm q-pb-sm">
           <q-avatar size="72px" class="q-mb-sm">
             <img v-if="solicitation.customer_photo" :src="solicitation.customer_photo" style="object-fit:cover" />
-            <span v-else class="text-weight-bold full-width full-height flex flex-center" :style="avatarColors[solicitation.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ solicitation.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
+            <span v-else class="full-width full-height flex flex-center" :style="avatarColors[solicitation.id % avatarColors.length]" style="font-size:20px;border-radius:50%">{{ solicitation.client_name?.slice(0,2).toUpperCase() ?? '??' }}</span>
           </q-avatar>
 
-          <div class="text-subtitle1 text-weight-bold text-text">{{ solicitation.client_name }}</div>
+          <div class="text-text font16 fontbold">{{ solicitation.client_name }}</div>
 
-          <div class="text-price q-mt-xs">{{ formatCurrency(solicitation.total_amount) }}</div>
-          <div class="text-caption text-grey-6">{{ periodLabel }}</div>
+          <div class="text-price text-primary font16 fontbold q-mt-xs">{{ formatCurrency(solicitation.total_amount) }}</div>
+          <div class="text-grey-6 font12">{{ periodLabel }}</div>
         </q-card-section>
 
-        <q-card-section class="column items-center q-pt-none q-pb-sm text-text">
-          <div class="text-body2 text-center">
-            <span class="text-weight-bold">{{ weekdayLabel + ' ,' + dayMonthLabel }}</span>
+        <q-card-section class="column items-center q-pt-none q-pb-sm text-text font14">
+          <div class="text-center">
+            <span class="fontbold">{{ weekdayLabel + ' ,' + dayMonthLabel }}</span>
           </div>
-          <div class="text-body2 text-center">
+          <div class="text-center">
             {{ $t('common.from') }} <strong>{{ solicitation.start_time?.slice(0, 5) }}</strong>
             {{ $t('common.to') }} <strong>{{ solicitation.end_time?.slice(0, 5) }}</strong>
           </div>
@@ -138,7 +138,7 @@
               :color="solicitation.offers_meal ? 'positive' : 'grey-5'"
               size="18px"
             />
-            <span class="text-body2 text-grey-7">
+            <span class="text-grey-7 font14">
               {{ solicitation.offers_meal
                 ? $t('provider.dashboard.solicitations.offers_meal')
                 : $t('provider.dashboard.solicitations.not_offers_meal') }}
@@ -148,17 +148,17 @@
 
         <q-card-section class="column items-center q-pt-xs q-pb-sm text-text">
           <div class="row items-center q-gutter-x-xs">
-            <q-icon name="mdi-map-marker" color="secondary" size="20px" />
-            <span class="text-body1 text-weight-bold">{{ solicitation.address?.district || 'N/A' }}</span>
+            <q-icon name="mdi-map-marker" color="primary" size="20px" />
+            <span class="font16 fontbold">{{ solicitation.address?.district || 'N/A' }}</span>
           </div>
-          <div class="text-caption text-grey-6 text-center q-mt-xs">
+          <div class="text-grey-6 text-center font12 q-mt-xs">
             {{ $t('provider.dashboard.solicitations.distance_prefix') }}
             <strong>{{ (solicitation.distance_km ?? 0) + ' km' }}</strong>
             {{ $t('provider.dashboard.solicitations.distance_suffix') }}
           </div>
-          <div class="q-mt-xs">
-            <span class="text-caption text-grey-6">{{ $t('provider.dashboard.solicitations.via_agenda_text') }} </span>
-            <span class="text-caption text-secondary text-weight-bold">{{ ' ' + $t('provider.dashboard.solicitations.via_agenda') }}</span>
+          <div class="q-mt-xs font12">
+            <span class="text-grey-6">{{ $t('provider.dashboard.solicitations.via_agenda_text') }} </span>
+            <span class="text-primary fontbold">{{ ' ' + $t('provider.dashboard.solicitations.via_agenda') }}</span>
           </div>
         </q-card-section>
 
@@ -189,15 +189,16 @@
           </div>
         </q-card-section>
 
-        <div class="q-pa-md">
-          <span class="text-caption text-warning text-weight-bold">{{ $t('provider.dashboard.solicitations.same_day_warning_label') }} </span>
-          <span class="text-caption text-grey-8">{{ $t('provider.dashboard.solicitations.same_day_warning') }}</span>
+        <div class="q-pa-md font12 text-primary">
+          <span>{{ $t('provider.dashboard.solicitations.same_day_warning_before') }}</span>
+          <span class="fontbold">{{ $t('provider.dashboard.solicitations.same_day_warning_bold') }}</span>
+          <span>{{ $t('provider.dashboard.solicitations.same_day_warning_after') }}</span>
         </div>
       </template>
 
       <template v-else-if="view === 'confirm-accept'">
         <q-card-section class="column items-center q-pt-xl q-pb-lg text-center">
-          <div class="confirm-title text-secondary text-weight-bold">
+          <div class="confirm-title text-secondary fontbold">
             {{ $t('provider.dashboard.solicitations.confirm_accept') }}
           </div>
         </q-card-section>
@@ -229,10 +230,10 @@
 
       <template v-else-if="view === 'confirm-reject'">
         <q-card-section class="column items-center q-pt-xl q-pb-md text-center">
-          <div class="confirm-title text-secondary text-weight-bold">
+          <div class="confirm-title text-secondary fontbold">
             {{ $t('provider.dashboard.solicitations.confirm_reject') }}
           </div>
-          <div class="q-mt-md text-body2 text-grey-7 confirm-tip">
+          <div class="q-mt-md text-grey-7 confirm-tip font13">
             <strong>{{ $t('provider.dashboard.solicitations.tip_label') }} </strong>
             {{ $t('provider.dashboard.solicitations.reject_tip') }}
           </div>
@@ -413,30 +414,23 @@ const openCancelDialog = () => {
 }
 
 .text-price {
-  font-size: 24px;
-  font-weight: 700;
   color: var(--q-secondary);
 }
 
 .btn-action {
   min-width: 110px;
-  font-weight: 700;
 }
 
 .btn-close-paid {
-  font-weight: 700;
-  font-size: 16px;
   padding: 10px 0;
 }
 
 .confirm-title {
-  font-size: 20px;
   line-height: 1.35;
   padding: 0 12px;
 }
 
 .confirm-tip {
-  font-size: 13px;
   line-height: 1.5;
   padding: 0 8px;
 }
@@ -448,16 +442,4 @@ const openCancelDialog = () => {
   margin-bottom: 4px;
 }
 
-.detail-label {
-  font-size: 12px;
-  font-weight: 600;
-}
-
-.detail-value {
-  font-size: 13px;
-}
-
-.total-value {
-  font-size: 22px;
-}
 </style>

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно