├── src ├── boot │ ├── .gitkeep │ ├── supabase.js │ └── axios.js ├── css │ └── app.css ├── App.vue ├── utils │ └── format.js ├── pages │ ├── category │ │ ├── table.js │ │ ├── Form.vue │ │ └── List.vue │ ├── Index.vue │ ├── Error404.vue │ ├── product │ │ ├── table.js │ │ ├── Form.vue │ │ ├── List.vue │ │ └── Public.vue │ ├── EmailConfirmation.vue │ ├── Me.vue │ ├── ResetPassword.vue │ ├── ForgotPassword.vue │ ├── Register.vue │ ├── Login.vue │ └── config │ │ └── Form.vue ├── composables │ ├── UseBrand.js │ ├── UseNotify.js │ ├── UseAuthUser.js │ └── UseApi.js ├── quasar.d.ts ├── layouts │ ├── LoginLayout.vue │ └── MainLayout.vue ├── components │ ├── DarkModeToggle.vue │ ├── EssentialLink.vue │ ├── CardDashbaord.vue │ └── DialogProductDetails.vue ├── router │ ├── index.js │ └── routes.js ├── index.template.html └── assets │ └── quasar-logo-vertical.svg ├── public ├── _netlify.toml ├── favicon.ico └── icons │ ├── icon-128x128.png │ ├── icon-192x192.png │ ├── icon-256x256.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon-128x128.png │ ├── ms-icon-144x144.png │ ├── apple-icon-120x120.png │ ├── apple-icon-152x152.png │ ├── apple-icon-167x167.png │ ├── apple-icon-180x180.png │ ├── maskable_icon_x512.png │ ├── apple-launch-1125x2436.png │ ├── apple-launch-1170x2532.png │ ├── apple-launch-1242x2208.png │ ├── apple-launch-1242x2688.png │ ├── apple-launch-1284x2778.png │ ├── apple-launch-1536x2048.png │ ├── apple-launch-1620x2160.png │ ├── apple-launch-1668x2224.png │ ├── apple-launch-1668x2388.png │ ├── apple-launch-2048x2732.png │ ├── apple-launch-750x1334.png │ ├── apple-launch-828x1792.png │ └── safari-pinned-tab.svg ├── iconSupabase.png ├── .eslintignore ├── .editorconfig ├── .postcssrc.js ├── .vscode ├── settings.json └── extensions.json ├── babel.config.js ├── src-pwa ├── pwa-flag.d.ts ├── custom-service-worker.js └── register-service-worker.js ├── .gitignore ├── jsconfig.json ├── README.md ├── package.json ├── .eslintrc.js └── quasar.conf.js /src/boot/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/css/app.css: -------------------------------------------------------------------------------- 1 | /* app global css */ 2 | -------------------------------------------------------------------------------- /public/_netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/" 4 | status = 200 -------------------------------------------------------------------------------- /iconSupabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/iconSupabase.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/icon-256x256.png -------------------------------------------------------------------------------- /public/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/icon-384x384.png -------------------------------------------------------------------------------- /public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/favicon-128x128.png -------------------------------------------------------------------------------- /public/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /src-bex/www 3 | /src-capacitor 4 | /src-cordova 5 | /.quasar 6 | /node_modules 7 | .eslintrc.js 8 | babel.config.js 9 | -------------------------------------------------------------------------------- /public/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/icons/apple-icon-167x167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-icon-167x167.png -------------------------------------------------------------------------------- /public/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/icons/maskable_icon_x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/maskable_icon_x512.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1125x2436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1125x2436.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1170x2532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1170x2532.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1242x2208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1242x2208.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1242x2688.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1242x2688.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1284x2778.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1284x2778.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1536x2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1536x2048.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1620x2160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1620x2160.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1668x2224.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1668x2224.png -------------------------------------------------------------------------------- /public/icons/apple-launch-1668x2388.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-1668x2388.png -------------------------------------------------------------------------------- /public/icons/apple-launch-2048x2732.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-2048x2732.png -------------------------------------------------------------------------------- /public/icons/apple-launch-750x1334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-750x1334.png -------------------------------------------------------------------------------- /public/icons/apple-launch-828x1792.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickmonteiro/quasar-estoque-supabase/HEAD/public/icons/apple-launch-828x1792.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 11 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.validation.template": false, 3 | "vetur.format.enable": false, 4 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], 5 | 6 | "vetur.experimental.templateInterpolationService": true 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/format.js: -------------------------------------------------------------------------------- 1 | const formatCurrency = (currency) => { 2 | const formatted = currency.toLocaleString('pt-br', { 3 | style: 'currency', 4 | currency: 'BRL' 5 | }) 6 | 7 | return formatted 8 | } 9 | 10 | export { 11 | formatCurrency 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/category/table.js: -------------------------------------------------------------------------------- 1 | const columnsCategory = [ 2 | { name: 'name', align: 'left', label: 'Name', field: 'name', sortable: true }, 3 | { name: 'actions', align: 'right', label: 'Actions', field: 'actions', sortable: true } 4 | ] 5 | 6 | export { 7 | columnsCategory 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | 5 | "octref.vetur" 6 | ], 7 | "unwantedRecommendations": [ 8 | "hookyqr.beautify", 9 | "dbaeumer.jshint", 10 | "ms-vscode.vscode-typescript-tslint-plugin" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = api => { 4 | return { 5 | presets: [ 6 | [ 7 | '@quasar/babel-preset-app', 8 | api.caller(caller => caller && caller.target === 'node') 9 | ? { targets: { node: 'current' } } 10 | : {} 11 | ] 12 | ] 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src-pwa/pwa-flag.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 4 | import "quasar/dist/types/feature-flag"; 5 | 6 | declare module "quasar/dist/types/feature-flag" { 7 | interface QuasarFeatureFlags { 8 | pwa: true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src-pwa/custom-service-worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file (which will be your service worker) 3 | * is picked up by the build system ONLY if 4 | * quasar.conf > pwa > workboxPluginMode is set to "InjectManifest" 5 | */ 6 | 7 | import { precacheAndRoute } from 'workbox-precaching' 8 | 9 | // Use with precache injection 10 | precacheAndRoute(self.__WB_MANIFEST) 11 | -------------------------------------------------------------------------------- /src/composables/UseBrand.js: -------------------------------------------------------------------------------- 1 | import { setCssVar } from 'quasar' 2 | export default function useBrand () { 3 | const setBrand = (primary, secondary) => { 4 | if (primary) { 5 | setCssVar('primary', primary) 6 | } 7 | 8 | if (secondary) { 9 | setCssVar('secondary', secondary) 10 | } 11 | } 12 | 13 | return { 14 | setBrand 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/quasar.d.ts: -------------------------------------------------------------------------------- 1 | // Forces TS to apply `@quasar/app` augmentations of `quasar` package 2 | // Removing this would break `quasar/wrappers` imports as those typings are declared 3 | // into `@quasar/app` 4 | // As a side effect, since `@quasar/app` reference `quasar` to augment it, 5 | // this declaration also apply `quasar` own 6 | // augmentations (eg. adds `$q` into Vue component context) 7 | /// 8 | -------------------------------------------------------------------------------- /src/boot/supabase.js: -------------------------------------------------------------------------------- 1 | import { createClient } from '@supabase/supabase-js' 2 | import useAuthUser from 'src/composables/UseAuthUser' 3 | 4 | const supabaseUrl = process.env.SUPABASE_URL 5 | const supabaseKey = process.env.SUPABASE_KEY 6 | const supabase = createClient(supabaseUrl, supabaseKey) 7 | 8 | supabase.auth.onAuthStateChange((event, session) => { 9 | const { user } = useAuthUser() 10 | 11 | user.value = session?.user || null 12 | }) 13 | 14 | export default function useSupabase () { 15 | return { supabase } 16 | } 17 | -------------------------------------------------------------------------------- /src/composables/UseNotify.js: -------------------------------------------------------------------------------- 1 | import { useQuasar } from 'quasar' 2 | export default function useNotify () { 3 | const $q = useQuasar() 4 | 5 | const notifySuccess = (message) => { 6 | $q.notify({ 7 | type: 'positive', 8 | message: message || 'All right !' 9 | }) 10 | } 11 | 12 | const notifyError = (message) => { 13 | $q.notify({ 14 | type: 'negative', 15 | message: message || 'Failed !' 16 | }) 17 | } 18 | 19 | return { 20 | notifySuccess, 21 | notifyError 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | node_modules 4 | 5 | # Quasar core related directories 6 | .quasar 7 | /dist 8 | 9 | # supabase 10 | /src/boot/supabase.js 11 | 12 | # Cordova related directories and files 13 | /src-cordova/node_modules 14 | /src-cordova/platforms 15 | /src-cordova/plugins 16 | /src-cordova/www 17 | 18 | # Capacitor related directories and files 19 | /src-capacitor/www 20 | /src-capacitor/node_modules 21 | 22 | # BEX related directories and files 23 | /src-bex/www 24 | /src-bex/js/core 25 | 26 | # Log files 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # Editor directories and files 32 | .idea 33 | *.suo 34 | *.ntvs* 35 | *.njsproj 36 | *.sln 37 | *.env 38 | -------------------------------------------------------------------------------- /src/pages/Error404.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 32 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "src/*": [ 6 | "src/*" 7 | ], 8 | "app/*": [ 9 | "*" 10 | ], 11 | "components/*": [ 12 | "src/components/*" 13 | ], 14 | "layouts/*": [ 15 | "src/layouts/*" 16 | ], 17 | "pages/*": [ 18 | "src/pages/*" 19 | ], 20 | "assets/*": [ 21 | "src/assets/*" 22 | ], 23 | "boot/*": [ 24 | "src/boot/*" 25 | ], 26 | "vue$": [ 27 | "node_modules/vue/dist/vue.runtime.esm-bundler.js" 28 | ] 29 | } 30 | }, 31 | "exclude": [ 32 | "dist", 33 | ".quasar", 34 | "node_modules" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/layouts/LoginLayout.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 34 | -------------------------------------------------------------------------------- /src/pages/product/table.js: -------------------------------------------------------------------------------- 1 | import { formatCurrency } from 'src/utils/format' 2 | import { ref } from 'vue' 3 | 4 | const columnsProduct = [ 5 | { name: 'img_url', align: 'left', label: 'Img', field: 'img_url', sortable: false }, 6 | { name: 'name', align: 'left', label: 'Name', field: 'name', sortable: true }, 7 | { name: 'amount', align: 'left', label: 'Amount', field: 'amount', sortable: true }, 8 | { name: 'price', align: 'left', label: 'Price', field: 'price', format: (val) => formatCurrency(val), sortable: true }, 9 | { name: 'actions', align: 'right', label: 'Actions', field: 'actions', sortable: false } 10 | ] 11 | 12 | const initialPagination = ref({ 13 | page: 1, 14 | rowsPerPage: 8 15 | }) 16 | 17 | export { 18 | columnsProduct, 19 | initialPagination 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/EmailConfirmation.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 30 | -------------------------------------------------------------------------------- /src/components/DarkModeToggle.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quasar Estoque Supabase (quasar-estoque-supabase) 2 | 3 | Project built on the Youtube Quasar + Supabase course: [Playlist on Youtube](https://www.youtube.com/watch?v=6ep8cy6pP74&list=PLBjvYfV_TvwIfgvouZCaLtgjYdrWQL02d&index=1) 4 | 5 | ## Init Config 6 | 7 | You are required to configure the ***quasar.conf.js*** file with your supabase credentials. 8 | 9 | ```js 10 | build: { 11 | env: { 12 | SUPABASE_URL: 'XXXX', 13 | SUPABASE_KEY: 'YYYY' 14 | } 15 | } 16 | ``` 17 | 18 | ## Install the dependencies 19 | ```bash 20 | yarn 21 | ``` 22 | 23 | ### Start the app in development mode (hot-code reloading, error reporting, etc.) 24 | ```bash 25 | quasar dev 26 | ``` 27 | 28 | ### Lint the files 29 | ```bash 30 | yarn run lint 31 | ``` 32 | 33 | ### Build the app for production 34 | ```bash 35 | quasar build 36 | ``` 37 | 38 | ### Customize the configuration 39 | See [Configuring quasar.conf.js](https://quasar.dev/quasar-cli/quasar-conf-js). 40 | -------------------------------------------------------------------------------- /src/components/EssentialLink.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 51 | -------------------------------------------------------------------------------- /src/boot/axios.js: -------------------------------------------------------------------------------- 1 | import { boot } from 'quasar/wrappers' 2 | import axios from 'axios' 3 | 4 | // Be careful when using SSR for cross-request state pollution 5 | // due to creating a Singleton instance here; 6 | // If any client changes this (global) instance, it might be a 7 | // good idea to move this instance creation inside of the 8 | // "export default () => {}" function below (which runs individually 9 | // for each client) 10 | const api = axios.create({ baseURL: 'https://api.example.com' }) 11 | 12 | export default boot(({ app }) => { 13 | // for use inside Vue files (Options API) through this.$axios and this.$api 14 | 15 | app.config.globalProperties.$axios = axios 16 | // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form) 17 | // so you won't necessarily have to import axios in each vue file 18 | 19 | app.config.globalProperties.$api = api 20 | // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form) 21 | // so you can easily perform requests against your app's API 22 | }) 23 | 24 | export { api } 25 | -------------------------------------------------------------------------------- /src/pages/Me.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 43 | -------------------------------------------------------------------------------- /src/components/CardDashbaord.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quasar-estoque-supabase", 3 | "version": "0.0.1", 4 | "description": "A Quasar Framework app", 5 | "productName": "Quasar Estoque Supabase", 6 | "author": "Patrick Monteiro ", 7 | "private": true, 8 | "scripts": { 9 | "lint": "eslint --ext .js,.vue ./", 10 | "test": "echo \"No test specified\" && exit 0" 11 | }, 12 | "dependencies": { 13 | "@quasar/extras": "^1.15.9", 14 | "@supabase/supabase-js": "^1.28.5", 15 | "axios": "^0.21.1", 16 | "core-js": "^3.6.5", 17 | "quasar": "^2.11.4", 18 | "uuid": "^8.3.2", 19 | "vue": "^3.0.0", 20 | "vue-router": "^4.0.0" 21 | }, 22 | "devDependencies": { 23 | "@babel/eslint-parser": "^7.13.14", 24 | "@quasar/app-webpack": "^3.6.2", 25 | "eslint": "^7.14.0", 26 | "eslint-config-standard": "^16.0.2", 27 | "eslint-plugin-import": "^2.19.1", 28 | "eslint-plugin-node": "^11.0.0", 29 | "eslint-plugin-promise": "^5.1.0", 30 | "eslint-plugin-vue": "^7.0.0", 31 | "eslint-webpack-plugin": "^2.4.0", 32 | "workbox-webpack-plugin": "^6.0.0" 33 | }, 34 | "browserslist": [ 35 | "last 10 Chrome versions", 36 | "last 10 Firefox versions", 37 | "last 4 Edge versions", 38 | "last 7 Safari versions", 39 | "last 8 Android versions", 40 | "last 8 ChromeAndroid versions", 41 | "last 8 FirefoxAndroid versions", 42 | "last 10 iOS versions", 43 | "last 5 Opera versions" 44 | ], 45 | "engines": { 46 | "node": ">= 12.22.1", 47 | "npm": ">= 6.13.4", 48 | "yarn": ">= 1.21.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src-pwa/register-service-worker.js: -------------------------------------------------------------------------------- 1 | import { register } from 'register-service-worker' 2 | import { Notify } from 'quasar' 3 | 4 | // The ready(), registered(), cached(), updatefound() and updated() 5 | // events passes a ServiceWorkerRegistration instance in their arguments. 6 | // ServiceWorkerRegistration: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration 7 | 8 | register(process.env.SERVICE_WORKER_FILE, { 9 | // The registrationOptions object will be passed as the second argument 10 | // to ServiceWorkerContainer.register() 11 | // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#Parameter 12 | 13 | // registrationOptions: { scope: './' }, 14 | 15 | ready (/* registration */) { 16 | // console.log('Service worker is active.') 17 | }, 18 | 19 | registered (/* registration */) { 20 | // console.log('Service worker has been registered.') 21 | }, 22 | 23 | cached (/* registration */) { 24 | // console.log('Content has been cached for offline use.') 25 | }, 26 | 27 | updatefound (/* registration */) { 28 | // console.log('New content is downloading.') 29 | }, 30 | 31 | updated (/* registration */) { 32 | // console.log('New content is available; please refresh.') 33 | Notify.create({ 34 | message: 'Nova atualização disponível', 35 | icon: 'mdi-cloud-download-outline', 36 | closeBtn: 'Atualizar', 37 | timeout: 10000, 38 | onDismiss () { 39 | location.reload(true) 40 | } 41 | }) 42 | }, 43 | 44 | offline () { 45 | // console.log('No internet connection found. App is running in offline mode.') 46 | }, 47 | 48 | error (/* err */) { 49 | // console.error('Error during service worker registration:', err) 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { route } from 'quasar/wrappers' 2 | import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router' 3 | import routes from './routes' 4 | import useAuthUser from 'src/composables/UseAuthUser' 5 | 6 | /* 7 | * If not building with SSR mode, you can 8 | * directly export the Router instantiation; 9 | * 10 | * The function below can be async too; either use 11 | * async/await or return a Promise which resolves 12 | * with the Router instance. 13 | */ 14 | 15 | export default route(function (/* { store, ssrContext } */) { 16 | const createHistory = process.env.SERVER 17 | ? createMemoryHistory 18 | : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory) 19 | 20 | const Router = createRouter({ 21 | scrollBehavior: () => ({ left: 0, top: 0 }), 22 | routes, 23 | 24 | // Leave this as is and make changes in quasar.conf.js instead! 25 | // quasar.conf.js -> build -> vueRouterMode 26 | // quasar.conf.js -> build -> publicPath 27 | history: createHistory(process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE) 28 | }) 29 | 30 | Router.beforeEach((to) => { 31 | const { isLoggedIn } = useAuthUser() 32 | 33 | if ( 34 | to.hash.includes('type=recovery') && 35 | to.name !== 'reset-password' 36 | ) { 37 | const accessToken = to.hash.split('&')[0] 38 | const token = accessToken.replace('#access_token=', '') 39 | return { name: 'reset-password', query: { token } } 40 | } 41 | if ( 42 | !isLoggedIn() && 43 | to.meta.requiresAuth && 44 | !Object.keys(to.query).includes('fromEmail') 45 | ) { 46 | return { name: 'login' } 47 | } 48 | }) 49 | 50 | return Router 51 | }) 52 | -------------------------------------------------------------------------------- /src/pages/ResetPassword.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 64 | -------------------------------------------------------------------------------- /src/pages/ForgotPassword.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 67 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | 2 | const routes = [ 3 | { 4 | path: '/', 5 | component: () => import('layouts/LoginLayout.vue'), 6 | children: [ 7 | { path: '', name: 'loginDefault', component: () => import('pages/Login.vue') }, 8 | { path: 'login', name: 'login', component: () => import('pages/Login.vue') }, 9 | { path: 'register', name: 'register', component: () => import('pages/Register.vue') }, 10 | { path: 'email-confirmation', name: 'email-confirmation', component: () => import('pages/EmailConfirmation.vue') }, 11 | { path: 'forgot-password', name: 'forgot-password', component: () => import('pages/ForgotPassword.vue') }, 12 | { path: 'reset-password', name: 'reset-password', component: () => import('pages/ResetPassword.vue') }, 13 | { path: 'product-public/:id', name: 'product-public', component: () => import('pages/product/Public.vue') } 14 | ] 15 | }, 16 | { 17 | path: '/', 18 | component: () => import('layouts/MainLayout.vue'), 19 | children: [ 20 | { path: 'me', name: 'me', component: () => import('pages/Me.vue') }, 21 | { path: 'category', name: 'category', component: () => import('pages/category/List.vue') }, 22 | { path: 'form-category/:id?', name: 'form-category', component: () => import('pages/category/Form.vue') }, 23 | { path: 'product', name: 'product', component: () => import('pages/product/List.vue') }, 24 | { path: 'form-product/:id?', name: 'form-product', component: () => import('pages/product/Form.vue') }, 25 | { path: 'form-config/:id?', name: 'form-config', component: () => import('pages/config/Form.vue') } 26 | // { path: '', component: () => import('pages/Index.vue') } 27 | ], 28 | meta: { 29 | requiresAuth: true 30 | } 31 | }, 32 | 33 | // Always leave this as last one, 34 | // but you can also remove it 35 | { 36 | path: '/:catchAll(.*)*', 37 | component: () => import('pages/Error404.vue') 38 | } 39 | ] 40 | 41 | export default routes 42 | -------------------------------------------------------------------------------- /src/components/DialogProductDetails.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 82 | -------------------------------------------------------------------------------- /src/pages/Register.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 92 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy 3 | // This option interrupts the configuration hierarchy at this file 4 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) 5 | root: true, 6 | 7 | parserOptions: { 8 | parser: '@babel/eslint-parser', 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: 'module' // Allows for the use of imports 11 | }, 12 | 13 | env: { 14 | browser: true 15 | }, 16 | 17 | // Rules order is important, please avoid shuffling them 18 | extends: [ 19 | // Base ESLint recommended rules 20 | // 'eslint:recommended', 21 | 22 | 23 | // Uncomment any of the lines below to choose desired strictness, 24 | // but leave only one uncommented! 25 | // See https://eslint.vuejs.org/rules/#available-rules 26 | 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) 27 | // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 28 | // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) 29 | 30 | 'standard' 31 | 32 | ], 33 | 34 | plugins: [ 35 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-file 36 | // required to lint *.vue files 37 | 'vue', 38 | 39 | ], 40 | 41 | globals: { 42 | ga: 'readonly', // Google Analytics 43 | cordova: 'readonly', 44 | __statics: 'readonly', 45 | __QUASAR_SSR__: 'readonly', 46 | __QUASAR_SSR_SERVER__: 'readonly', 47 | __QUASAR_SSR_CLIENT__: 'readonly', 48 | __QUASAR_SSR_PWA__: 'readonly', 49 | process: 'readonly', 50 | Capacitor: 'readonly', 51 | chrome: 'readonly' 52 | }, 53 | 54 | // add your custom rules here 55 | rules: { 56 | // allow async-await 57 | 'generator-star-spacing': 'off', 58 | // allow paren-less arrow functions 59 | 'arrow-parens': 'off', 60 | 'one-var': 'off', 61 | 'no-void': 'off', 62 | 'multiline-ternary': 'off', 63 | 64 | 'import/first': 'off', 65 | 'import/named': 'error', 66 | 'import/namespace': 'error', 67 | 'import/default': 'error', 68 | 'import/export': 'error', 69 | 'import/extensions': 'off', 70 | 'import/no-unresolved': 'off', 71 | 'import/no-extraneous-dependencies': 'off', 72 | 'prefer-promise-reject-errors': 'off', 73 | 74 | 75 | // allow debugger during development only 76 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/pages/Login.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 98 | -------------------------------------------------------------------------------- /src/pages/category/Form.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 98 | -------------------------------------------------------------------------------- /src/composables/UseAuthUser.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import useSupabase from 'boot/supabase' 3 | // user is set outside of the useAuthUser function 4 | // so that it will act as global state and always refer to a single user 5 | 6 | // o usuário é definido fora da função useAuthUser para que atue como um estado global 7 | // e sempre se refira a um único usuário 8 | const user = ref(null) 9 | 10 | export default function useAuthUser () { 11 | const { supabase } = useSupabase() 12 | /** 13 | * Login with email and password 14 | */ 15 | const login = async ({ email, password }) => { 16 | const { user, error } = await supabase.auth.signIn({ email, password }) 17 | if (error) throw error 18 | return user 19 | } 20 | 21 | /** 22 | * Login with google, github, etc 23 | */ 24 | const loginWithSocialProvider = async (provider) => { 25 | const { user, error } = await supabase.auth.signIn({ provider }) 26 | if (error) throw error 27 | return user 28 | } 29 | 30 | /** 31 | * Logout 32 | */ 33 | const logout = async () => { 34 | const { error } = await supabase.auth.signOut() 35 | if (error) throw error 36 | } 37 | 38 | /** 39 | * Check if the user is logged in or not 40 | */ 41 | const isLoggedIn = () => { 42 | return !!user.value 43 | } 44 | 45 | /** 46 | * Register 47 | */ 48 | const register = async ({ email, password, ...meta }) => { 49 | const { user, error } = await supabase.auth.signUp( 50 | { email, password }, 51 | { 52 | // arbitrary meta data is passed as the second argument under a data key 53 | // to the Supabase signUp method 54 | data: meta, 55 | // the to redirect to after the user confirms their email 56 | // window.location wouldn't be available if we were rendering server side 57 | // but since we're all on the client it will work fine 58 | redirectTo: `${window.location.origin}/me?fromEmail=registrationConfirmation"` 59 | }) 60 | if (error) throw error 61 | return user 62 | } 63 | 64 | /** 65 | * Update user email, password, or meta data 66 | */ 67 | const update = async (data) => { 68 | const { user, error } = await supabase.auth.update(data) 69 | if (error) throw error 70 | return user 71 | } 72 | 73 | /** 74 | * Send user an email to reset their password 75 | * (ie. support "Forgot Password?") 76 | */ 77 | const sendPasswordRestEmail = async (email) => { 78 | const { user, error } = await supabase.auth.api.resetPasswordForEmail(email) 79 | if (error) throw error 80 | return user 81 | } 82 | 83 | const resetPassword = async (accessToken, newPassword) => { 84 | const { user, error } = await supabase.auth.api.updateUser( 85 | accessToken, 86 | { password: newPassword } 87 | ) 88 | if (error) throw error 89 | return user 90 | } 91 | 92 | return { 93 | user, 94 | login, 95 | loginWithSocialProvider, 96 | isLoggedIn, 97 | logout, 98 | register, 99 | update, 100 | sendPasswordRestEmail, 101 | resetPassword 102 | // maybeHandleEmailConfirmation, 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= productName %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /src/layouts/MainLayout.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 146 | -------------------------------------------------------------------------------- /src/pages/category/List.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 123 | -------------------------------------------------------------------------------- /src/pages/config/Form.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 130 | -------------------------------------------------------------------------------- /src/composables/UseApi.js: -------------------------------------------------------------------------------- 1 | import useSupabase from 'src/boot/supabase' 2 | import useAuthUser from './UseAuthUser' 3 | import { v4 as uuidv4 } from 'uuid' 4 | import { useRoute } from 'vue-router' 5 | import useBrand from 'src/composables/UseBrand' 6 | import { ref } from 'vue' 7 | import { useQuasar } from 'quasar' 8 | 9 | const brand = ref({ 10 | primary: '', 11 | secondary: '', 12 | name: '', 13 | phone: '', 14 | paralax_url: '' 15 | }) 16 | 17 | export default function useApi () { 18 | const { supabase } = useSupabase() 19 | const { user } = useAuthUser() 20 | const route = useRoute() 21 | const { setBrand } = useBrand() 22 | const $q = useQuasar() 23 | 24 | const list = async (table) => { 25 | const { data, error } = await supabase 26 | .from(table) 27 | .select('*') 28 | if (error) throw error 29 | return data 30 | } 31 | 32 | const listPublic = async (table, userId, columnFilter = '', filter = '') => { 33 | const { data, error } = await supabase 34 | .from(table) 35 | .select('*') 36 | .eq('user_id', userId) 37 | .eq(columnFilter, filter) 38 | if (error) throw error 39 | return data 40 | } 41 | 42 | const fetchCount = async (table, userId) => { 43 | const { data, error, count } = await supabase 44 | .from(table) 45 | .select('*', { count: 'exact' }) 46 | .eq('user_id', userId) 47 | if (error) throw error 48 | return { 49 | data, 50 | count 51 | } 52 | } 53 | 54 | const getById = async (table, id) => { 55 | const { data, error } = await supabase 56 | .from(table) 57 | .select('*') 58 | .eq('id', id) 59 | if (error) throw error 60 | return data[0] 61 | } 62 | 63 | const post = async (table, form) => { 64 | const { data, error } = await supabase 65 | .from(table) 66 | .insert([ 67 | { 68 | ...form, 69 | user_id: user.value.id 70 | } 71 | ]) 72 | if (error) throw error 73 | return data 74 | } 75 | 76 | const update = async (table, form) => { 77 | const { data, error } = await supabase 78 | .from(table) 79 | .update({ ...form }) 80 | .match({ id: form.id }) 81 | if (error) throw error 82 | return data 83 | } 84 | 85 | const remove = async (table, id) => { 86 | const { data, error } = await supabase 87 | .from(table) 88 | .delete() 89 | .match({ id }) 90 | if (error) throw error 91 | return data 92 | } 93 | 94 | const uploadImg = async (file, storage) => { 95 | const fileName = uuidv4() 96 | const { error } = supabase 97 | .storage 98 | .from(storage) 99 | .upload(fileName, file, { 100 | cacheControl: '3600', 101 | upsert: false 102 | }) 103 | const publicUrl = await getUrlPublic(fileName, storage) 104 | if (error) throw error 105 | return publicUrl 106 | } 107 | 108 | const getUrlPublic = async (fileName, storage) => { 109 | const { publicURL, error } = supabase 110 | .storage 111 | .from(storage) 112 | .getPublicUrl(fileName) 113 | if (error) throw error 114 | return publicURL 115 | } 116 | 117 | const getBrand = async () => { 118 | const id = route.params.id || user?.value?.id 119 | if (id) { 120 | $q.loading.show({ 121 | backgroundColor: 'dark' 122 | }) 123 | const { data, error } = await supabase 124 | .from('config') 125 | .select('*') 126 | .eq('user_id', id) 127 | if (error) throw error 128 | if (data.length > 0) { 129 | brand.value = data[0] 130 | setBrand(brand.value.primary, brand.value.secondary) 131 | } 132 | $q.loading.hide() 133 | return brand 134 | } 135 | } 136 | 137 | return { 138 | list, 139 | listPublic, 140 | fetchCount, 141 | getById, 142 | post, 143 | update, 144 | remove, 145 | uploadImg, 146 | getBrand, 147 | brand 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/assets/quasar-logo-vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /src/pages/product/Form.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 157 | -------------------------------------------------------------------------------- /src/pages/product/List.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 173 | -------------------------------------------------------------------------------- /src/pages/product/Public.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 170 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file runs in a Node context (it's NOT transpiled by Babel), so use only 3 | * the ES6 features that are supported by your Node version. https://node.green/ 4 | */ 5 | 6 | // Configuration for your app 7 | // https://quasar.dev/quasar-cli/quasar-conf-js 8 | 9 | /* eslint-env node */ 10 | const ESLintPlugin = require('eslint-webpack-plugin') 11 | const { configure } = require('quasar/wrappers') 12 | 13 | module.exports = configure(function (ctx) { 14 | return { 15 | // https://quasar.dev/quasar-cli/supporting-ts 16 | supportTS: false, 17 | 18 | // https://quasar.dev/quasar-cli/prefetch-feature 19 | // preFetch: true, 20 | 21 | // app boot file (/src/boot) 22 | // --> boot files are part of "main.js" 23 | // https://quasar.dev/quasar-cli/boot-files 24 | boot: [ 25 | 'supabase', 26 | 'axios' 27 | ], 28 | 29 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css 30 | css: [ 31 | 'app.css' 32 | ], 33 | 34 | // https://github.com/quasarframework/quasar/tree/dev/extras 35 | extras: [ 36 | // 'ionicons-v4', 37 | // 'mdi-v5', 38 | // 'fontawesome-v5', 39 | // 'eva-icons', 40 | // 'themify', 41 | // 'line-awesome', 42 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 43 | 44 | 'roboto-font', // optional, you are not bound to it 45 | 'material-icons', // optional, you are not bound to it 46 | 'mdi-v5' 47 | ], 48 | 49 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build 50 | build: { 51 | vueRouterMode: 'history', // available values: 'hash', 'history' 52 | env: { 53 | SUPABASE_URL: 'https://fiopknivucqeguhjpqdo.supabase.co', 54 | SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzOTUyMTcwOSwiZXhwIjoxOTU1MDk3NzA5fQ.prM0rQ-qe7mHLFgm_508rCaC47lVJpx7YQqFnPUsHVs' 55 | }, 56 | // transpile: false, 57 | // publicPath: '/', 58 | 59 | // Add dependencies for transpiling with Babel (Array of string/regex) 60 | // (from node_modules, which are by default not transpiled). 61 | // Applies only if "transpile" is set to true. 62 | // transpileDependencies: [], 63 | 64 | // rtl: true, // https://quasar.dev/options/rtl-support 65 | // preloadChunks: true, 66 | // showProgress: false, 67 | // gzip: true, 68 | // analyze: true, 69 | 70 | // Options below are automatically set depending on the env, set them if you want to override 71 | // extractCSS: false, 72 | 73 | // https://quasar.dev/quasar-cli/handling-webpack 74 | // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain 75 | chainWebpack (chain) { 76 | chain.plugin('eslint-webpack-plugin') 77 | .use(ESLintPlugin, [{ extensions: ['js', 'vue'] }]) 78 | } 79 | }, 80 | 81 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer 82 | devServer: { 83 | server: { 84 | type: 'http' 85 | }, 86 | port: 8080, 87 | open: true // opens browser window automatically 88 | }, 89 | 90 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework 91 | framework: { 92 | config: { 93 | brand: { 94 | primary: '#2c9c6a', 95 | secondary: '#26A69A', 96 | accent: '#9C27B0', 97 | dark: '#1d1d1d', 98 | positive: '#21BA45', 99 | negative: '#C10015', 100 | info: '#31CCEC', 101 | warning: '#F2C037' 102 | } 103 | }, 104 | 105 | // iconSet: 'material-icons', // Quasar icon set 106 | // lang: 'en-US', // Quasar language pack 107 | 108 | // For special cases outside of where the auto-import strategy can have an impact 109 | // (like functional components as one of the examples), 110 | // you can manually specify Quasar components/directives to be available everywhere: 111 | // 112 | // components: [], 113 | // directives: [], 114 | 115 | // Quasar plugins 116 | plugins: [ 117 | 'Dialog', 118 | 'Notify', 119 | 'Loading', 120 | 'LocalStorage' 121 | ] 122 | }, 123 | 124 | // animations: 'all', // --- includes all animations 125 | // https://quasar.dev/options/animations 126 | animations: 'all', 127 | 128 | // https://quasar.dev/quasar-cli/developing-ssr/configuring-ssr 129 | ssr: { 130 | pwa: false, 131 | 132 | // manualStoreHydration: true, 133 | // manualPostHydrationTrigger: true, 134 | 135 | prodPort: 3000, // The default port that the production server should use 136 | // (gets superseded if process.env.PORT is specified at runtime) 137 | 138 | maxAge: 1000 * 60 * 60 * 24 * 30, 139 | // Tell browser when a file from the server should expire from cache (in ms) 140 | 141 | chainWebpackWebserver (chain) { 142 | chain.plugin('eslint-webpack-plugin') 143 | .use(ESLintPlugin, [{ extensions: ['js'] }]) 144 | }, 145 | 146 | middlewares: [ 147 | ctx.prod ? 'compression' : '', 148 | 'render' // keep this as last one 149 | ] 150 | }, 151 | 152 | // https://quasar.dev/quasar-cli/developing-pwa/configuring-pwa 153 | pwa: { 154 | workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' 155 | workboxOptions: { skipWaiting: true, clientsClaim: true }, // only for GenerateSW 156 | 157 | // for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts]) 158 | // if using workbox in InjectManifest mode 159 | chainWebpackCustomSW (chain) { 160 | chain.plugin('eslint-webpack-plugin') 161 | .use(ESLintPlugin, [{ extensions: ['js'] }]) 162 | }, 163 | 164 | manifest: { 165 | name: 'Quasar Estoque Supabase', 166 | short_name: 'Quasar Estoque Supabase', 167 | description: 'Estoque fácil na palma da sua mão', 168 | display: 'standalone', 169 | orientation: 'portrait', 170 | background_color: '#ffffff', 171 | theme_color: '#2c9c6a', 172 | categories: ['utilities', 'productivity', 'personalization'], 173 | icons: [ 174 | { 175 | src: 'icons/icon-128x128.png', 176 | sizes: '128x128', 177 | type: 'image/png' 178 | }, 179 | { 180 | src: 'icons/icon-192x192.png', 181 | sizes: '192x192', 182 | type: 'image/png' 183 | }, 184 | { 185 | src: 'icons/icon-256x256.png', 186 | sizes: '256x256', 187 | type: 'image/png' 188 | }, 189 | { 190 | src: 'icons/icon-384x384.png', 191 | sizes: '384x384', 192 | type: 'image/png' 193 | }, 194 | { 195 | src: 'icons/icon-512x512.png', 196 | sizes: '512x512', 197 | type: 'image/png' 198 | }, 199 | { 200 | src: 'icons/maskable_icon_x512.png', 201 | sizes: '512x512', 202 | type: 'image/png', 203 | purpose: 'maskable' 204 | } 205 | ] 206 | } 207 | }, 208 | 209 | // Full list of options: https://quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova 210 | cordova: { 211 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing 212 | }, 213 | 214 | // Full list of options: https://quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor 215 | capacitor: { 216 | hideSplashscreen: true 217 | }, 218 | 219 | // Full list of options: https://quasar.dev/quasar-cli/developing-electron-apps/configuring-electron 220 | electron: { 221 | bundler: 'packager', // 'packager' or 'builder' 222 | 223 | packager: { 224 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 225 | 226 | // OS X / Mac App Store 227 | // appBundleId: '', 228 | // appCategoryType: '', 229 | // osxSign: '', 230 | // protocol: 'myapp://path', 231 | 232 | // Windows only 233 | // win32metadata: { ... } 234 | }, 235 | 236 | builder: { 237 | // https://www.electron.build/configuration/configuration 238 | 239 | appId: 'quasar-estoque-supabase' 240 | }, 241 | 242 | // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain 243 | chainWebpackMain (chain) { 244 | chain.plugin('eslint-webpack-plugin') 245 | .use(ESLintPlugin, [{ extensions: ['js'] }]) 246 | }, 247 | 248 | // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain 249 | chainWebpackPreload (chain) { 250 | chain.plugin('eslint-webpack-plugin') 251 | .use(ESLintPlugin, [{ extensions: ['js'] }]) 252 | } 253 | } 254 | } 255 | }) 256 | -------------------------------------------------------------------------------- /public/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------