Merge to master #21
23
.babelrc
23
.babelrc
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [
|
||||
"dynamic-import-node"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": "commonjs",
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -1,9 +0,0 @@
|
|||
/dist
|
||||
/src-bex/www
|
||||
/src-capacitor
|
||||
/src-cordova
|
||||
/.quasar
|
||||
/node_modules
|
||||
.eslintrc.js
|
||||
babel.config.js
|
||||
/src-ssr
|
103
.eslintrc.js
103
.eslintrc.js
|
@ -1,103 +0,0 @@
|
|||
const { resolve } = require('path');
|
||||
module.exports = {
|
||||
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||
// This option interrupts the configuration hierarchy at this file
|
||||
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||
root: true,
|
||||
|
||||
// https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
|
||||
// Must use parserOptions instead of "parser" to allow vue-eslint-parser to keep working
|
||||
// `parser: 'vue-eslint-parser'` is already included with any 'plugin:vue/**' config and should be omitted
|
||||
parserOptions: {
|
||||
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#configuration
|
||||
// https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#eslint
|
||||
// Needed to make the parser take into account 'vue' files
|
||||
extraFileExtensions: ['.vue'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
project: resolve(__dirname, './tsconfig.json'),
|
||||
tsconfigRootDir: __dirname,
|
||||
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
},
|
||||
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
|
||||
// Rules order is important, please avoid shuffling them
|
||||
extends: [
|
||||
// Base ESLint recommended rules
|
||||
// 'eslint:recommended',
|
||||
|
||||
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
|
||||
// ESLint typescript rules
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
// consider disabling this class of rules if linting takes too long
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
|
||||
// Uncomment any of the lines below to choose desired strictness,
|
||||
// but leave only one uncommented!
|
||||
// See https://eslint.vuejs.org/rules/#available-rules
|
||||
// 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||
'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||
|
||||
// https://github.com/prettier/eslint-config-prettier#installation
|
||||
// usage with Prettier, provided by 'eslint-config-prettier'.
|
||||
'prettier',
|
||||
],
|
||||
|
||||
plugins: [
|
||||
// required to apply rules which need type information
|
||||
'@typescript-eslint',
|
||||
|
||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||
// required to lint *.vue files
|
||||
'vue',
|
||||
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
|
||||
// Prettier has not been included as plugin to avoid performance impact
|
||||
// add it as an extension for your IDE
|
||||
],
|
||||
|
||||
globals: {
|
||||
ga: 'readonly', // Google Analytics
|
||||
cordova: 'readonly',
|
||||
__statics: 'readonly',
|
||||
__QUASAR_SSR__: 'readonly',
|
||||
__QUASAR_SSR_SERVER__: 'readonly',
|
||||
__QUASAR_SSR_CLIENT__: 'readonly',
|
||||
__QUASAR_SSR_PWA__: 'readonly',
|
||||
process: 'readonly',
|
||||
Capacitor: 'readonly',
|
||||
chrome: 'readonly',
|
||||
},
|
||||
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
|
||||
// TypeScript
|
||||
quotes: ['warn', 'single', { avoidEscape: true }],
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
|
||||
// allow debugger during development only
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.spec.{js,ts}'],
|
||||
extends: [
|
||||
// Add Cypress-specific lint rules, globals and Cypress plugin
|
||||
// See https://github.com/cypress-io/eslint-plugin-cypress#rules
|
||||
'plugin:cypress/recommended',
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
.DS_Store
|
||||
.thumbs.db
|
||||
node_modules
|
||||
|
||||
# Quasar core related directories
|
||||
.quasar
|
||||
/dist
|
||||
|
||||
# Cordova related directories and files
|
||||
/src-cordova/node_modules
|
||||
/src-cordova/platforms
|
||||
/src-cordova/plugins
|
||||
/src-cordova/www
|
||||
|
||||
# Capacitor related directories and files
|
||||
/src-capacitor/www
|
||||
/src-capacitor/node_modules
|
||||
|
||||
# BEX related directories and files
|
||||
/src-bex/www
|
||||
/src-bex/js/core
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.vscode
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
|
@ -1,8 +0,0 @@
|
|||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
require('autoprefixer'),
|
||||
],
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"endOfLine": "auto"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"]
|
||||
}
|
37
README.md
37
README.md
|
@ -1,37 +0,0 @@
|
|||
# Salix (salix-front)
|
||||
|
||||
Salix front-end
|
||||
|
||||
## Install the dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
||||
|
||||
```bash
|
||||
quasar dev
|
||||
```
|
||||
|
||||
### Lint the files
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Format the files
|
||||
|
||||
```bash
|
||||
npm run format
|
||||
```
|
||||
|
||||
### Build the app for production
|
||||
|
||||
```bash
|
||||
quasar build
|
||||
```
|
||||
|
||||
### Customize the configuration
|
||||
|
||||
See [Configuring quasar.conf.js](https://quasar.dev/quasar-cli/quasar-conf-js).
|
|
@ -1,18 +0,0 @@
|
|||
/* eslint-env node */
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const fs = require('fs-extra');
|
||||
let extend = undefined;
|
||||
|
||||
/**
|
||||
* The .babelrc file has been created to assist Jest for transpiling.
|
||||
* You should keep your application's babel rules in this file.
|
||||
*/
|
||||
|
||||
if (fs.existsSync('./.babelrc')) {
|
||||
extend = './.babelrc';
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
presets: ['@quasar/babel-preset-app'],
|
||||
extends: extend,
|
||||
};
|
15
cypress.json
15
cypress.json
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:8080/",
|
||||
"fixturesFolder": "test/cypress/fixtures",
|
||||
"integrationFolder": "test/cypress/integration",
|
||||
"pluginsFile": "test/cypress/plugins/index.js",
|
||||
"screenshotsFolder": "test/cypress/screenshots",
|
||||
"supportFile": "test/cypress/support/index.js",
|
||||
"videosFolder": "test/cypress/videos",
|
||||
"video": true,
|
||||
"component": {
|
||||
"componentFolder": "src",
|
||||
"testFiles": "**/*.spec.js",
|
||||
"supportFile": "test/cypress/support/unit.js"
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
const esModules = ['quasar', 'quasar/lang', 'lodash-es'].join('|');
|
||||
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
globals: {
|
||||
__DEV__: true,
|
||||
// TODO: Remove if resolved natively
|
||||
// See https://github.com/vuejs/vue-jest/issues/175
|
||||
'vue-jest': {
|
||||
pug: { doctype: 'html' },
|
||||
},
|
||||
// Remove if using `const enums`
|
||||
// See https://huafu.github.io/ts-jest/user/config/isolatedModules#example
|
||||
'ts-jest': {
|
||||
isolatedModules: true,
|
||||
},
|
||||
},
|
||||
// noStackTrace: true,
|
||||
// bail: true,
|
||||
// cache: false,
|
||||
// verbose: true,
|
||||
// watch: true,
|
||||
collectCoverage: false,
|
||||
coverageDirectory: '<rootDir>/test/jest/coverage',
|
||||
collectCoverageFrom: [
|
||||
'<rootDir>/src/**/*.vue',
|
||||
'<rootDir>/src/**/*.js',
|
||||
'<rootDir>/src/**/*.ts',
|
||||
'<rootDir>/src/**/*.jsx',
|
||||
'<rootDir>/src/**/*.tsx',
|
||||
],
|
||||
coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
// branches: 50,
|
||||
// functions: 50,
|
||||
// lines: 50,
|
||||
// statements: 50
|
||||
},
|
||||
},
|
||||
testMatch: [
|
||||
// Matches tests in any subfolder of 'src' or into 'test/jest/__tests__'
|
||||
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
|
||||
// '<rootDir>/test/jest/__tests__/**/*.(spec|test).+(ts|js)?(x)',
|
||||
'<rootDir>/src/**/__tests__/*.(spec|test).+(ts|js)?(x)',
|
||||
],
|
||||
// Extension-less imports of components are resolved to .ts files by TS,
|
||||
// grating correct type-checking in test files.
|
||||
// Being 'vue' the first moduleFileExtension option, the very same imports
|
||||
// will be resolved to .vue files by Jest, if both .vue and .ts files are
|
||||
// in the same folder.
|
||||
// This guarantee a great dev experience both for testing and type-checking.
|
||||
// See https://github.com/vuejs/vue-jest/issues/188#issuecomment-620750728
|
||||
moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'],
|
||||
moduleNameMapper: {
|
||||
'^quasar$': 'quasar/dist/quasar.esm.prod.js',
|
||||
'^~/(.*)$': '<rootDir>/$1',
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
'^app/(.*)$': '<rootDir>/$1',
|
||||
'^components/(.*)$': '<rootDir>/src/components/$1',
|
||||
'^composables/(.*)$': '<rootDir>/src/composables/$1',
|
||||
'^layouts/(.*)$': '<rootDir>/src/layouts/$1',
|
||||
'^pages/(.*)$': '<rootDir>/src/pages/$1',
|
||||
'^assets/(.*)$': '<rootDir>/src/assets/$1',
|
||||
'^boot/(.*)$': '<rootDir>/src/boot/$1',
|
||||
'.*css$': '@quasar/quasar-app-extension-testing-unit-jest/stub.css',
|
||||
},
|
||||
transform: {
|
||||
// See https://jestjs.io/docs/en/configuration.html#transformignorepatterns-array-string
|
||||
[`^(${esModules}).+\\.js$`]: 'babel-jest',
|
||||
'^.+\\.(ts|js|html)$': 'ts-jest',
|
||||
// vue-jest uses find-babel-file, which searches by this order:
|
||||
// (async) .babelrc, .babelrc.js, package.json, babel.config.js
|
||||
// (sync) .babelrc, .babelrc.js, babel.config.js, package.json
|
||||
// https://github.com/tleunen/find-babel-config/issues/33
|
||||
'.*\\.vue$': 'vue-jest',
|
||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
},
|
||||
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
|
||||
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
|
||||
};
|
File diff suppressed because it is too large
Load Diff
60
package.json
60
package.json
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"name": "salix-front",
|
||||
"version": "0.0.1",
|
||||
"description": "Salix front-end",
|
||||
"productName": "Salix",
|
||||
"author": "Salix",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.ts,.vue ./",
|
||||
"format": "prettier --write \"**/*.{js,ts,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
|
||||
"test:unit:coverage": "jest --coverage",
|
||||
"test:unit": "jest --watchAll",
|
||||
"test:e2e": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress open --config-file cypress.json\"",
|
||||
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\"",
|
||||
"serve:test:coverage": "quasar serve test/jest/coverage/lcov-report/ --port 8788",
|
||||
"concurrently:dev:jest": "concurrently \"quasar dev\" \"jest --watch\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.6.5",
|
||||
"quasar": "^2.0.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue-i18n": "^9.0.0",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@intlify/vue-i18n-loader": "^4.1.0",
|
||||
"@quasar/app": "^3.0.0",
|
||||
"@quasar/quasar-app-extension-testing-e2e-cypress": "^4.0.1",
|
||||
"@quasar/quasar-app-extension-testing-unit-jest": "^3.0.0-alpha.8",
|
||||
"@types/node": "^12.20.21",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"eslint": "^7.14.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-jest": "^25.2.2",
|
||||
"eslint-plugin-vue": "^7.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"eslint-plugin-cypress": "^2.11.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 10 Chrome versions",
|
||||
"last 10 Firefox versions",
|
||||
"last 4 Edge versions",
|
||||
"last 7 Safari versions",
|
||||
"last 8 Android versions",
|
||||
"last 8 ChromeAndroid versions",
|
||||
"last 8 FirefoxAndroid versions",
|
||||
"last 10 iOS versions",
|
||||
"last 5 Opera versions"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.22.1",
|
||||
"npm": ">= 6.13.4",
|
||||
"yarn": ">= 1.21.1"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 63 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
233
quasar.conf.js
233
quasar.conf.js
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
||||
* the ES6 features that are supported by your Node version. https://node.green/
|
||||
*/
|
||||
|
||||
// Configuration for your app
|
||||
// https://quasar.dev/quasar-cli/quasar-conf-js
|
||||
|
||||
/* eslint-env node */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { configure } = require('quasar/wrappers');
|
||||
|
||||
module.exports = configure(function (ctx) {
|
||||
return {
|
||||
// https://quasar.dev/quasar-cli/supporting-ts
|
||||
supportTS: {
|
||||
tsCheckerConfig: {
|
||||
eslint: {
|
||||
enabled: true,
|
||||
files: './src/**/*.{ts,tsx,js,jsx,vue}',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// https://quasar.dev/quasar-cli/prefetch-feature
|
||||
// preFetch: true,
|
||||
|
||||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://quasar.dev/quasar-cli/boot-files
|
||||
boot: ['i18n', 'axios'],
|
||||
|
||||
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css
|
||||
css: ['app.scss'],
|
||||
|
||||
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||
extras: [
|
||||
// 'ionicons-v4',
|
||||
// 'mdi-v5',
|
||||
// 'fontawesome-v5',
|
||||
// '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://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
|
||||
build: {
|
||||
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||
|
||||
// transpile: false,
|
||||
// publicPath: '/',
|
||||
|
||||
// Add dependencies for transpiling with Babel (Array of string/regex)
|
||||
// (from node_modules, which are by default not transpiled).
|
||||
// Applies only if "transpile" is set to true.
|
||||
// transpileDependencies: [],
|
||||
|
||||
// rtl: true, // https://quasar.dev/options/rtl-support
|
||||
// preloadChunks: true,
|
||||
// showProgress: false,
|
||||
// gzip: true,
|
||||
// analyze: true,
|
||||
|
||||
// Options below are automatically set depending on the env, set them if you want to override
|
||||
// extractCSS: false,
|
||||
|
||||
// https://quasar.dev/quasar-cli/handling-webpack
|
||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||
chainWebpack(/* chain */) {
|
||||
//
|
||||
},
|
||||
},
|
||||
|
||||
// Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer
|
||||
devServer: {
|
||||
server: {
|
||||
type: 'http',
|
||||
},
|
||||
port: 8080,
|
||||
open: true, // opens browser window automatically
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000',
|
||||
logLevel: 'debug',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework
|
||||
framework: {
|
||||
config: {},
|
||||
|
||||
// iconSet: 'material-icons', // Quasar icon set
|
||||
// lang: 'en-US', // Quasar language pack
|
||||
|
||||
// For special cases outside of where the auto-import strategy can have an impact
|
||||
// (like functional components as one of the examples),
|
||||
// you can manually specify Quasar components/directives to be available everywhere:
|
||||
//
|
||||
// components: [],
|
||||
// directives: [],
|
||||
|
||||
// Quasar plugins
|
||||
plugins: ['Notify'],
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
// https://quasar.dev/options/animations
|
||||
animations: [],
|
||||
|
||||
// https://quasar.dev/quasar-cli/developing-ssr/configuring-ssr
|
||||
ssr: {
|
||||
pwa: false,
|
||||
|
||||
// manualStoreHydration: true,
|
||||
// manualPostHydrationTrigger: true,
|
||||
|
||||
prodPort: 3000, // The default port that the production server should use
|
||||
// (gets superseded if process.env.PORT is specified at runtime)
|
||||
|
||||
maxAge: 1000 * 60 * 60 * 24 * 30,
|
||||
// Tell browser when a file from the server should expire from cache (in ms)
|
||||
|
||||
chainWebpackWebserver(/* chain */) {
|
||||
//
|
||||
},
|
||||
|
||||
middlewares: [
|
||||
ctx.prod ? 'compression' : '',
|
||||
'render', // keep this as last one
|
||||
],
|
||||
},
|
||||
|
||||
// https://quasar.dev/quasar-cli/developing-pwa/configuring-pwa
|
||||
pwa: {
|
||||
workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
|
||||
workboxOptions: {}, // only for GenerateSW
|
||||
|
||||
// for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
|
||||
// if using workbox in InjectManifest mode
|
||||
chainWebpackCustomSW(/* chain */) {
|
||||
//
|
||||
},
|
||||
|
||||
manifest: {
|
||||
name: 'Salix',
|
||||
short_name: 'Salix',
|
||||
description: 'Salix front-end',
|
||||
display: 'standalone',
|
||||
orientation: 'portrait',
|
||||
background_color: '#ffffff',
|
||||
theme_color: '#027be3',
|
||||
icons: [
|
||||
{
|
||||
src: 'icons/icon-128x128.png',
|
||||
sizes: '128x128',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-256x256.png',
|
||||
sizes: '256x256',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-384x384.png',
|
||||
sizes: '384x384',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/icon-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Full list of options: https://quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova
|
||||
cordova: {
|
||||
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||
},
|
||||
|
||||
// Full list of options: https://quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor
|
||||
capacitor: {
|
||||
hideSplashscreen: true,
|
||||
},
|
||||
|
||||
// Full list of options: https://quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
|
||||
electron: {
|
||||
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: 'salix-front',
|
||||
},
|
||||
|
||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||
chainWebpack(/* chain */) {
|
||||
// do something with the Electron main process Webpack cfg
|
||||
// extendWebpackMain also available besides this chainWebpackMain
|
||||
},
|
||||
|
||||
// "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
|
||||
chainWebpackPreload(/* chain */) {
|
||||
// do something with the Electron main process Webpack cfg
|
||||
// extendWebpackPreload also available besides this chainWebpackPreload
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"@quasar/testing-unit-jest": {
|
||||
"babel": "babelrc",
|
||||
"options": ["scripts", "typescript"]
|
||||
},
|
||||
"@quasar/testing-e2e-cypress": {
|
||||
"options": ["scripts"]
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"unit-jest": {
|
||||
"runnerCommand": "jest --ci"
|
||||
},
|
||||
"e2e-cypress": {
|
||||
"runnerCommand": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
|
||||
},
|
||||
"unit-cypress": {
|
||||
"runnerCommand": "cypress run-ct"
|
||||
}
|
||||
}
|
24
src/App.vue
24
src/App.vue
|
@ -1,24 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { useQuasar } from 'quasar';
|
||||
const quasar = useQuasar();
|
||||
|
||||
quasar.iconMapFn = (iconName) => {
|
||||
if (iconName.startsWith('vn:')) {
|
||||
const name = iconName.substring(3);
|
||||
|
||||
return {
|
||||
cls: `icon-${name}`,
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.body--light {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
|
@ -1,70 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="Capa_1" inkscape:version="0.91 r13725" sodipodi:docname="logo.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:ns="&ns_sfw;" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 400 168.6"
|
||||
style="enable-background:new 0 0 400 168.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#3D3D3F;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#8EBB27;}
|
||||
.st2{fill:#8EBB27;}
|
||||
.st3{fill:#F19300;}
|
||||
</style>
|
||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1" fit-margin-bottom="0" fit-margin-left="0" fit-margin-right="0" fit-margin-top="0" gridtolerance="10" guidetolerance="10" id="namedview41" inkscape:current-layer="Capa_1" inkscape:cx="200" inkscape:cy="84.28212" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="1016" inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="1920" inkscape:window-y="27" inkscape:zoom="3.09" objecttolerance="10" pagecolor="#ffffff" showgrid="false">
|
||||
</sodipodi:namedview>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M106.1,40L92.3,0h10.9l5.6,20.6l0.5,1.7c0.7,2.5,1.2,4.5,1.6,6.2c0.2-0.8,0.4-1.8,0.7-2.9
|
||||
c0.3-1.1,0.7-2.6,1.2-4.3L118.7,0h10.8l-13.9,40H106.1z"/>
|
||||
<path class="st1" d="M386.1,40h-9.8c0-0.5,0.1-1,0.1-1.5l0.2-1.6c-1.7,1.4-3.5,2.4-5.2,3c-1.7,0.6-3.5,1-5.3,1
|
||||
c-2.8,0-4.9-0.8-6.1-2.3c-1.2-1.6-1.5-3.7-0.7-6.3c0.7-2.4,1.9-4.4,3.6-6c1.7-1.5,4-2.6,6.8-3.2c1.5-0.3,3.5-0.7,5.8-1.1
|
||||
c3.5-0.5,5.4-1.3,5.7-2.4l0.2-0.7c0.2-0.9,0.1-1.5-0.4-2c-0.5-0.4-1.4-0.7-2.7-0.7c-1.4,0-2.6,0.3-3.5,0.8c-1,0.6-1.7,1.4-2.2,2.5
|
||||
h-8.9c1.4-3.3,3.5-5.8,6.2-7.5c2.7-1.6,6.2-2.4,10.5-2.4c2.6,0,4.7,0.3,6.4,1c1.6,0.6,2.8,1.6,3.4,2.9c0.4,0.9,0.6,2,0.6,3.3
|
||||
c-0.1,1.3-0.5,3.3-1.3,6.2l-3.1,11.2c-0.4,1.3-0.5,2.4-0.5,3.2c0,0.8,0.2,1.3,0.7,1.5L386.1,40z M379.4,26.1
|
||||
c-0.9,0.5-2.3,0.9-4.3,1.3c-1,0.2-1.7,0.3-2.2,0.5c-1.3,0.3-2.2,0.7-2.8,1.2c-0.6,0.5-1.1,1.2-1.3,2c-0.3,1.1-0.2,1.9,0.3,2.5
|
||||
c0.5,0.6,1.2,1,2.3,1c1.7,0,3.1-0.5,4.4-1.4c1.3-1,2.2-2.2,2.6-3.7L379.4,26.1z"/>
|
||||
<path class="st1" d="M337.3,40l8.3-29.5h9.3l-1.4,5.2c1.6-2,3.3-3.5,5.1-4.4c1.8-0.9,3.9-1.4,6.3-1.5l-2.7,9.6
|
||||
c-0.4-0.1-0.8-0.1-1.2-0.1c-0.4,0-0.8,0-1.1,0c-1.5,0-2.8,0.2-3.9,0.7c-1.1,0.4-2.1,1.1-2.9,2.1c-0.5,0.6-1,1.5-1.5,2.6
|
||||
c-0.5,1.1-1.1,3-1.8,5.6l-2.8,9.9H337.3z"/>
|
||||
<path class="st1" d="M340.8,10.5L332.5,40h-9.5l1.1-4.1c-1.6,1.6-3.3,2.9-4.9,3.6c-1.7,0.8-3.5,1.2-5.4,1.2
|
||||
c-3.3,0-5.5-0.8-6.7-2.5c-1.2-1.7-1.3-4.2-0.4-7.4l5.7-20.3h9.7L317.6,27c-0.7,2.4-0.8,4.1-0.5,5c0.4,0.9,1.3,1.4,2.8,1.4
|
||||
c1.7,0,3.1-0.6,4.1-1.7c1.1-1.1,2-2.9,2.7-5.5l4.4-15.8H340.8z"/>
|
||||
<path class="st1" d="M290.1,16.3l1.6-5.8h4l2.3-8.3h9.7l-2.3,8.3h5l-1.6,5.8h-5l-3.6,12.8c-0.5,2-0.7,3.3-0.3,3.9
|
||||
c0.3,0.6,1.2,1,2.6,1l0.7,0l0.5,0l-1.7,6.2c-1.1,0.2-2.1,0.3-3.1,0.5c-1,0.1-2,0.2-2.9,0.2c-3.4,0-5.4-0.8-6.2-2.5
|
||||
c-0.8-1.6-0.4-5.1,1.1-10.5l3.2-11.4H290.1z"/>
|
||||
<path class="st1" d="M283.5,40h-9.8c0-0.5,0.1-1,0.1-1.5L274,37c-1.7,1.4-3.5,2.4-5.2,3c-1.7,0.6-3.5,1-5.3,1
|
||||
c-2.8,0-4.9-0.8-6.1-2.3c-1.2-1.6-1.5-3.7-0.7-6.3c0.7-2.4,1.9-4.4,3.6-6c1.7-1.5,4-2.6,6.8-3.2c1.5-0.3,3.5-0.7,5.8-1.1
|
||||
c3.5-0.5,5.4-1.3,5.7-2.4l0.2-0.7c0.2-0.9,0.1-1.5-0.4-2c-0.5-0.4-1.4-0.7-2.7-0.7c-1.4,0-2.6,0.3-3.5,0.8c-1,0.6-1.7,1.4-2.2,2.5
|
||||
H261c1.4-3.3,3.5-5.8,6.2-7.5c2.7-1.6,6.2-2.4,10.5-2.4c2.6,0,4.7,0.3,6.4,1c1.6,0.6,2.8,1.6,3.4,2.9c0.4,0.9,0.6,2,0.6,3.3
|
||||
c-0.1,1.3-0.5,3.3-1.3,6.2l-3.1,11.2c-0.4,1.3-0.5,2.4-0.5,3.2c0,0.8,0.2,1.3,0.7,1.5L283.5,40z M276.7,26.1
|
||||
c-0.9,0.5-2.3,0.9-4.3,1.3c-1,0.2-1.7,0.3-2.2,0.5c-1.3,0.3-2.2,0.7-2.8,1.2c-0.6,0.5-1.1,1.2-1.3,2c-0.3,1.1-0.2,1.9,0.3,2.5
|
||||
c0.5,0.6,1.2,1,2.3,1c1.7,0,3.1-0.5,4.4-1.4c1.3-1,2.2-2.2,2.6-3.7L276.7,26.1z"/>
|
||||
<path class="st0" d="M219.6,0l-11.2,40h-9.7l1.1-3.9c-1.5,1.6-3.1,2.8-4.8,3.6c-1.6,0.8-3.4,1.2-5.3,1.2c-3.7,0-6.3-1.4-7.8-4.3
|
||||
c-1.5-2.9-1.6-6.6-0.3-11.2c1.3-4.7,3.5-8.4,6.7-11.4c3.1-2.9,6.5-4.4,10.1-4.4c1.9,0,3.6,0.4,4.8,1.2c1.3,0.8,2.2,1.9,2.8,3.5
|
||||
L210,0H219.6z M189.8,24.9c-0.7,2.6-0.8,4.7-0.2,6.1c0.6,1.4,1.8,2.1,3.7,2.1c1.8,0,3.4-0.7,4.8-2.1c1.3-1.4,2.4-3.4,3.1-6.1
|
||||
c0.7-2.5,0.7-4.4,0.1-5.8c-0.6-1.4-1.8-2-3.7-2c-1.7,0-3.3,0.7-4.7,2.1C191.5,20.6,190.4,22.5,189.8,24.9z"/>
|
||||
<path class="st0" d="M153.6,40l8.3-29.5h9.3l-1.4,5.2c1.6-2,3.3-3.5,5.1-4.4c1.8-0.9,7.9-1.4,10.3-1.5l-2.7,9.6
|
||||
c-0.4-0.1-0.8-0.1-1.2-0.1c-0.4,0-0.8,0-1.1,0c-1.5,0-6.8,0.2-7.9,0.7c-1.1,0.4-2.1,1.1-2.9,2.1c-0.5,0.6-1,1.5-1.5,2.6
|
||||
c-0.5,1.1-1.1,3-1.8,5.6l-2.8,9.9H153.6z"/>
|
||||
<path class="st0" d="M143.5,30.7h9.3c-1.8,3.2-4.2,5.7-7.2,7.5c-3,1.8-6.4,2.7-10.2,2.7c-4.6,0-7.8-1.4-9.7-4.2
|
||||
c-1.9-2.8-2.2-6.6-0.8-11.4c1.4-4.9,3.8-8.8,7.3-11.6c3.5-2.9,7.5-4.3,12-4.3c4.7,0,8,1.5,9.8,4.3c1.9,2.9,2.1,6.9,0.7,12
|
||||
l-0.3,1.1l-0.2,0.6h-20c-0.6,2.1-0.6,3.7,0,4.8c0.6,1.1,1.8,1.6,3.5,1.6c1.3,0,2.4-0.3,3.4-0.8C142.1,32.6,142.9,31.8,143.5,30.7z
|
||||
M135.4,22.1l11,0c0.5-1.9,0.4-3.4-0.3-4.4c-0.7-1.1-1.8-1.6-3.5-1.6c-1.6,0-3,0.5-4.3,1.6C137.1,18.6,136.1,20.1,135.4,22.1z"/>
|
||||
<path class="st2" d="M241.2,40.4l-8.4-24.6l-8.5,24.6h-9.6l12.6-40h10.8L244,21l0.5,1.7c0.7,2.5,1.2,4.5,1.6,6.2l0.7-2.9
|
||||
c0.3-1.1,0.7-2.6,1.2-4.3l5.9-21.2h10.8l-13.9,40H241.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M106.1,54.4h4.8l48.9,113.9h-5.9L137,129H79.9l-16.8,39.3H57L106.1,54.4z M135.3,124.2l-26.8-62.7l-26.9,62.7
|
||||
H135.3z"/>
|
||||
<path class="st3" d="M178.1,168.3V54.4h5.6v108.7h69.8v5.1H178.1z"/>
|
||||
<path class="st3" d="M271.1,168.3V54.4h5.6v113.9H271.1z"/>
|
||||
<path class="st3" d="M300.2,54.4l42,53.6l42-53.6h6.4l-45.4,57.7l44.1,56.1H383l-40.7-52l-40.7,52h-6.7l44.1-56.1l-45.4-57.7
|
||||
H300.2z"/>
|
||||
<g>
|
||||
<path class="st3" d="M5.8,168.3L5.3,163l0.2,2.7L5.3,163c0.4,0,10.4-1.1,18.9-11.8c10.5-13.1,14.1-35.2,10.5-63.9
|
||||
C31,57.7,35.4,34.8,47.6,19.1C60.3,3,76.6,0.9,77.3,0.8l0.6,5.3c-0.1,0-11.9,1.6-22.4,12.1c-14,14-19.3,37.7-15.5,68.4
|
||||
c3.8,30.7-0.1,53.6-11.8,68.1C18.3,167.1,6.3,168.2,5.8,168.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 4.4 KiB |
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="40mm" height="40mm" viewBox="0 0 40 40" version="1.1" id="svg823" inkscape:version="0.92.4 (5da689c313, 2019-01-14)" sodipodi:docname="logo.svg">
|
||||
<defs id="defs817"/>
|
||||
<sodipodi:namedview id="base" pagecolor="#2f2f2f" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="5.635625" inkscape:cx="70.551181" inkscape:cy="75.590551" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1920" inkscape:window-height="1043" inkscape:window-x="1920" inkscape:window-y="0" inkscape:window-maximized="1"/>
|
||||
<metadata id="metadata820">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-257)">
|
||||
<path style="fill:#88bd32;stroke-width:0.83333331" inkscape:connector-curvature="0" id="path4" d="m 27.583333,261.08333 c 0.25,-0.0833 0.5,-0.16666 0.75,-0.16666 L 39,259.5 l -0.166667,6.08333 c -0.166666,5 -3.083333,9.5 -6.666666,10.5 -0.25,0.0833 -0.5,0.16667 -0.75,0.16667 L 20.75,277.66667 l 0.166667,-6.08334 c 0.166666,-5.08333 3.083333,-9.5 6.666666,-10.5 z" class="st0"/>
|
||||
<path style="fill:#88bd32;stroke-width:0.83333331" inkscape:connector-curvature="0" id="path6" d="m 5.9166667,281.91667 c 0.1666667,-0.0833 0.4166667,-0.0833 0.5833334,-0.0833 L 14.25,280.75 14.16667,285.08333 C 14.08334,288.75 11.91667,292 9.3333364,292.75 c -0.25,0.0833 -0.4166667,0.0833 -0.5833334,0.0833 L 1.0000001,293.91667 1.0833334,289.5 c 0.1666667,-3.58333 2.25,-6.91667 4.8333333,-7.58333 z" class="st0"/>
|
||||
<g id="g10" style="fill:#f7931e;fill-opacity:1;stroke:none;stroke-opacity:1" transform="matrix(0.83333333,0,0,0.83333333,8.0000002e-8,257)">
|
||||
<path style="fill:#f7931e;fill-opacity:1;stroke:none;stroke-opacity:1" inkscape:connector-curvature="0" id="path8" d="m 12,48 c -0.4,0 -0.7,-0.3 -0.7,-0.6 0,-0.4 0.2,-0.7 0.6,-0.8 0,0 3,-0.3 5.5,-3.4 3,-3.8 4.1,-10.1 3,-18.4 C 19.3,16.3 20.6,9.7 24.1,5.3 27.8,0.6 32.5,0 32.7,0 c 0.4,0 0.7,0.2 0.8,0.6 0,0.4 -0.2,0.7 -0.6,0.8 0,0 -4.3,0.6 -7.6,4.7 -3.3,4.2 -4.4,10.4 -3.4,18.5 1.1,8.8 0,15.4 -3.4,19.5 -2.8,3.5 -6.2,3.9 -6.5,3.9 0.1,0 0.1,0 0,0 z" class="st1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
|
@ -1,48 +0,0 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
const { getToken } = useSession();
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$axios: AxiosInstance;
|
||||
}
|
||||
}
|
||||
|
||||
// Be careful when using SSR for cross-request state pollution
|
||||
// due to creating a Singleton instance here;
|
||||
// If any client changes this (global) instance, it might be a
|
||||
// good idea to move this instance creation inside of the
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api = axios.create({ baseURL: 'https://api.example.com' });
|
||||
|
||||
axios.interceptors.request.use(
|
||||
function (context) {
|
||||
const token = getToken();
|
||||
|
||||
if (token.length && context.headers) {
|
||||
context.headers.Authorization = token;
|
||||
}
|
||||
|
||||
return context;
|
||||
},
|
||||
function (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
app.config.globalProperties.$axios = axios;
|
||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||
// so you won't necessarily have to import axios in each vue file
|
||||
|
||||
app.config.globalProperties.$api = api;
|
||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||
// so you can easily perform requests against your app's API
|
||||
});
|
||||
|
||||
export { api };
|
|
@ -1,16 +0,0 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
import messages from 'src/i18n';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'en',
|
||||
messages,
|
||||
});
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// Set i18n instance on app
|
||||
app.use(i18n);
|
||||
});
|
||||
|
||||
export { i18n };
|
|
@ -1,58 +0,0 @@
|
|||
<template>
|
||||
<q-header class="bg-dark" color="white" elevated bordered>
|
||||
<q-toolbar class="q-py-sm q-px-md">
|
||||
<q-btn flat @click="$emit('toggle-drawer')" round dense icon="menu" />
|
||||
<router-link to="/">
|
||||
<q-btn flat round class="q-ml-xs" v-if="$q.screen.gt.xs">
|
||||
<q-avatar square size="md">
|
||||
<q-img src="~/assets/logo_icon.svg" alt="Logo" />
|
||||
</q-avatar>
|
||||
</q-btn>
|
||||
</router-link>
|
||||
<q-toolbar-title shrink class="text-weight-bold">Salix</q-toolbar-title>
|
||||
<q-space></q-space>
|
||||
<div class="q-pl-sm q-gutter-sm row items-center no-wrap">
|
||||
<q-btn v-if="$q.screen.gt.xs" dense flat size="md" icon="add">
|
||||
<q-icon name="arrow_drop_down" size="s" />
|
||||
<q-menu>
|
||||
<q-list style="min-width: 150px">
|
||||
<q-item clickable>
|
||||
<q-item-section>New customer</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable>
|
||||
<q-item-section>New ticket</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<q-btn v-if="$q.screen.gt.xs" dense flat round size="md" icon="notifications" />
|
||||
<q-btn dense flat no-wrap>
|
||||
<q-avatar size="lg">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
<q-tooltip>Account</q-tooltip>
|
||||
<q-icon name="arrow_drop_down" size="s" />
|
||||
<UserPanel />
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import UserPanel from 'src/components/UserPanel.vue';
|
||||
|
||||
const session = useSession();
|
||||
const state = useState();
|
||||
const user = state.getUser();
|
||||
const token = session.getToken();
|
||||
|
||||
defineEmits<{
|
||||
(event: 'toggle-drawer'): void;
|
||||
}>();
|
||||
</script>
|
|
@ -1,91 +0,0 @@
|
|||
<template>
|
||||
<q-menu>
|
||||
<div class="row no-wrap q-pa-md">
|
||||
<div class="column">
|
||||
<div class="text-h6 q-mb-md">{{ t('components.userPanel.settings') }}</div>
|
||||
<q-toggle
|
||||
:label="t(`globals.lang['${locale}']`)"
|
||||
icon="public"
|
||||
color="orange"
|
||||
false-value="es"
|
||||
true-value="en"
|
||||
v-model="locale"
|
||||
/>
|
||||
<q-toggle v-model="darkMode" checked-icon="dark_mode" color="orange" unchecked-icon="light_mode" />
|
||||
|
||||
<q-btn color="orange" outline size="sm" label="Settings" icon="settings" />
|
||||
</div>
|
||||
|
||||
<q-separator vertical inset class="q-mx-lg" />
|
||||
|
||||
<div class="column items-center" style="min-width: 150px">
|
||||
<q-avatar size="80px">
|
||||
<q-img
|
||||
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
|
||||
<div class="text-subtitle1 q-mt-md">
|
||||
<strong>{{ user.nickname }}</strong>
|
||||
</div>
|
||||
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.username }}</div>
|
||||
|
||||
<q-btn color="orange" flat label="Log Out" size="sm" icon="logout" @click="logout()" v-close-popup />
|
||||
</div>
|
||||
</div>
|
||||
</q-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, computed } from 'vue';
|
||||
import { Dark, useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const state = useState();
|
||||
const session = useSession();
|
||||
const router = useRouter();
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
const darkMode = computed({
|
||||
get(): boolean {
|
||||
return Dark.isActive;
|
||||
},
|
||||
set(value: boolean): void {
|
||||
Dark.set(value);
|
||||
},
|
||||
});
|
||||
|
||||
const user = state.getUser();
|
||||
const token = session.getToken();
|
||||
|
||||
onMounted(() => {
|
||||
axios
|
||||
.get('/api/accounts/acl')
|
||||
.then(({ data }) => {
|
||||
state.setUser({
|
||||
id: data.user.id,
|
||||
username: data.user.name,
|
||||
nickname: data.user.nickname,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
quasar.notify({
|
||||
message: t('errors.statusUnauthorized'),
|
||||
type: 'negative',
|
||||
});
|
||||
logout();
|
||||
});
|
||||
});
|
||||
|
||||
function logout(): void {
|
||||
session.destroy();
|
||||
router.push('/login');
|
||||
}
|
||||
</script>
|
|
@ -1,28 +0,0 @@
|
|||
import { describe, expect, it } from '@jest/globals';
|
||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
import UserPanel from '../UserPanel.vue';
|
||||
|
||||
// Specify here Quasar config you'll need to test your component
|
||||
installQuasarPlugin();
|
||||
const routerPushMock = jest.fn();
|
||||
|
||||
jest.mock('vue-router', () => ({
|
||||
useRouter: () => ({
|
||||
push: routerPushMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('UserPanel', () => {
|
||||
it('should have the function logout defined', () => {
|
||||
const wrapper = mount(UserPanel, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
|
||||
expect(vm.logout).toBeDefined();
|
||||
});
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
export interface Todo {
|
||||
id: number;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
totalCount: number;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/* import store from '@/store';
|
||||
|
||||
export function useRole() {
|
||||
function hasAny(roles: string[]): boolean {
|
||||
const roleStore: string[] = store.state.roles;
|
||||
|
||||
for (const role of roles) {
|
||||
if (roleStore.indexOf(role) !== -1) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
hasAny,
|
||||
};
|
||||
}
|
||||
*/
|
|
@ -1,57 +0,0 @@
|
|||
import { useState } from './useState';
|
||||
|
||||
interface Session {
|
||||
token: string;
|
||||
keepLogin: boolean;
|
||||
}
|
||||
|
||||
interface useSession {
|
||||
getToken: () => string;
|
||||
setToken: (data: Session) => void;
|
||||
destroy: () => void;
|
||||
isLoggedIn: () => boolean;
|
||||
}
|
||||
|
||||
export function useSession(): useSession {
|
||||
function getToken(): string {
|
||||
const localToken = localStorage.getItem('token');
|
||||
const sessionToken = sessionStorage.getItem('token');
|
||||
|
||||
return localToken || sessionToken || '';
|
||||
}
|
||||
|
||||
function setToken(data: Session): void {
|
||||
if (data.keepLogin) {
|
||||
localStorage.setItem('token', data.token);
|
||||
} else {
|
||||
sessionStorage.setItem('token', data.token);
|
||||
}
|
||||
}
|
||||
|
||||
function destroy(): void {
|
||||
localStorage.removeItem('token');
|
||||
sessionStorage.getItem('token');
|
||||
|
||||
const { setUser } = useState();
|
||||
|
||||
setUser({
|
||||
id: 0,
|
||||
username: '',
|
||||
nickname: '',
|
||||
});
|
||||
}
|
||||
|
||||
function isLoggedIn() {
|
||||
const localToken = localStorage.getItem('token');
|
||||
const sessionToken = sessionStorage.getItem('token');
|
||||
|
||||
return !!(localToken || sessionToken);
|
||||
}
|
||||
|
||||
return {
|
||||
getToken,
|
||||
setToken,
|
||||
destroy,
|
||||
isLoggedIn,
|
||||
};
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
import { ref, Ref, computed, ComputedRef } from 'vue';
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
}
|
||||
|
||||
interface useState {
|
||||
getUser: () => ComputedRef<User>;
|
||||
setUser: (data: User) => void;
|
||||
getRoles: () => ComputedRef<string[]>;
|
||||
setRoles: (data: string[]) => void;
|
||||
}
|
||||
|
||||
const user: Ref<User> = ref({
|
||||
id: 0,
|
||||
username: '',
|
||||
nickname: '',
|
||||
});
|
||||
|
||||
const roles: Ref<string[]> = ref([]);
|
||||
|
||||
export function useState(): useState {
|
||||
function getUser(): ComputedRef<User> {
|
||||
return computed(() => {
|
||||
return {
|
||||
id: user.value.id,
|
||||
username: user.value.username,
|
||||
nickname: user.value.nickname,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function setUser(data: User): void {
|
||||
user.value = {
|
||||
id: data.id,
|
||||
username: data.username,
|
||||
nickname: data.nickname,
|
||||
};
|
||||
}
|
||||
|
||||
function getRoles(): ComputedRef<string[]> {
|
||||
return computed(() => {
|
||||
return roles.value;
|
||||
});
|
||||
}
|
||||
|
||||
function setRoles(data: string[]): void {
|
||||
roles.value = data;
|
||||
}
|
||||
|
||||
return {
|
||||
getUser,
|
||||
setUser,
|
||||
getRoles,
|
||||
setRoles,
|
||||
};
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// app global css in SCSS form
|
||||
@import './icons.scss';
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -1,393 +0,0 @@
|
|||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url('fonts/icomoon.eot?cjtmya');
|
||||
src: url('fonts/icomoon.eot?cjtmya#iefix') format('embedded-opentype'),
|
||||
url('fonts/icomoon.ttf?cjtmya') format('truetype'), url('fonts/icomoon.woff?cjtmya') format('woff'),
|
||||
url('fonts/icomoon.svg?cjtmya#icomoon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
[class^='icon-'],
|
||||
[class*=' icon-'] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'icomoon' !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-frozen:before {
|
||||
content: '\e900';
|
||||
}
|
||||
.icon-Person:before {
|
||||
content: '\e901';
|
||||
}
|
||||
.icon-handmadeArtificial:before {
|
||||
content: '\e902';
|
||||
}
|
||||
.icon-fruit:before {
|
||||
content: '\e903';
|
||||
}
|
||||
.icon-funeral:before {
|
||||
content: '\e904';
|
||||
}
|
||||
.icon-noPayMethod:before {
|
||||
content: '\e905';
|
||||
}
|
||||
.icon-preserved:before {
|
||||
content: '\e906';
|
||||
}
|
||||
.icon-greenery:before {
|
||||
content: '\e907';
|
||||
}
|
||||
.icon-planta:before {
|
||||
content: '\e908';
|
||||
}
|
||||
.icon-handmade:before {
|
||||
content: '\e909';
|
||||
}
|
||||
.icon-accessory:before {
|
||||
content: '\e90a';
|
||||
}
|
||||
.icon-artificial:before {
|
||||
content: '\e90b';
|
||||
}
|
||||
.icon-flower:before {
|
||||
content: '\e90c';
|
||||
}
|
||||
.icon-fixedPrice:before {
|
||||
content: '\e90d';
|
||||
}
|
||||
.icon-addperson:before {
|
||||
content: '\e90e';
|
||||
}
|
||||
.icon-supplierfalse:before {
|
||||
content: '\e90f';
|
||||
}
|
||||
.icon-invoice-out:before {
|
||||
content: '\e910';
|
||||
}
|
||||
.icon-invoice-in:before {
|
||||
content: '\e911';
|
||||
}
|
||||
.icon-invoice-in-create:before {
|
||||
content: '\e912';
|
||||
}
|
||||
.icon-basketadd:before {
|
||||
content: '\e913';
|
||||
}
|
||||
.icon-basket:before {
|
||||
content: '\e914';
|
||||
}
|
||||
.icon-uniE915:before {
|
||||
content: '\e915';
|
||||
}
|
||||
.icon-uniE916:before {
|
||||
content: '\e916';
|
||||
}
|
||||
.icon-uniE917:before {
|
||||
content: '\e917';
|
||||
}
|
||||
.icon-uniE918:before {
|
||||
content: '\e918';
|
||||
}
|
||||
.icon-uniE919:before {
|
||||
content: '\e919';
|
||||
}
|
||||
.icon-uniE91A:before {
|
||||
content: '\e91a';
|
||||
}
|
||||
.icon-isTooLittle:before {
|
||||
content: '\e91b';
|
||||
}
|
||||
.icon-deliveryprices:before {
|
||||
content: '\e91c';
|
||||
}
|
||||
.icon-onlinepayment:before {
|
||||
content: '\e91d';
|
||||
}
|
||||
.icon-risk:before {
|
||||
content: '\e91e';
|
||||
}
|
||||
.icon-noweb:before {
|
||||
content: '\e91f';
|
||||
}
|
||||
.icon-no036:before {
|
||||
content: '\e920';
|
||||
}
|
||||
.icon-disabled:before {
|
||||
content: '\e921';
|
||||
}
|
||||
.icon-treatments:before {
|
||||
content: '\e922';
|
||||
}
|
||||
.icon-invoice:before {
|
||||
content: '\e923';
|
||||
}
|
||||
.icon-photo:before {
|
||||
content: '\e924';
|
||||
}
|
||||
.icon-supplier:before {
|
||||
content: '\e925';
|
||||
}
|
||||
.icon-languaje:before {
|
||||
content: '\e926';
|
||||
}
|
||||
.icon-credit:before {
|
||||
content: '\e927';
|
||||
}
|
||||
.icon-client:before {
|
||||
content: '\e928';
|
||||
}
|
||||
.icon-shipment-01:before {
|
||||
content: '\e929';
|
||||
}
|
||||
.icon-account:before {
|
||||
content: '\e92a';
|
||||
}
|
||||
.icon-inventory:before {
|
||||
content: '\e92b';
|
||||
}
|
||||
.icon-unavailable:before {
|
||||
content: '\e92c';
|
||||
}
|
||||
.icon-wiki:before {
|
||||
content: '\e92d';
|
||||
}
|
||||
.icon-attach:before {
|
||||
content: '\e92e';
|
||||
}
|
||||
.icon-exit:before {
|
||||
content: '\e92f';
|
||||
}
|
||||
.icon-anonymous:before {
|
||||
content: '\e930';
|
||||
}
|
||||
.icon-net:before {
|
||||
content: '\e931';
|
||||
}
|
||||
.icon-buyrequest:before {
|
||||
content: '\e932';
|
||||
}
|
||||
.icon-thermometer:before {
|
||||
content: '\e933';
|
||||
}
|
||||
.icon-entry:before {
|
||||
content: '\e934';
|
||||
}
|
||||
.icon-deletedTicket:before {
|
||||
content: '\e935';
|
||||
}
|
||||
.icon-logout:before {
|
||||
content: '\e936';
|
||||
}
|
||||
.icon-catalog:before {
|
||||
content: '\e937';
|
||||
}
|
||||
.icon-agency:before {
|
||||
content: '\e938';
|
||||
}
|
||||
.icon-delivery:before {
|
||||
content: '\e939';
|
||||
}
|
||||
.icon-wand:before {
|
||||
content: '\e93a';
|
||||
}
|
||||
.icon-buscaman:before {
|
||||
content: '\e93b';
|
||||
}
|
||||
.icon-pbx:before {
|
||||
content: '\e93c';
|
||||
}
|
||||
.icon-calendar:before {
|
||||
content: '\e93d';
|
||||
}
|
||||
.icon-splitline:before {
|
||||
content: '\e93e';
|
||||
}
|
||||
.icon-consignatarios:before {
|
||||
content: '\e93f';
|
||||
}
|
||||
.icon-tax:before {
|
||||
content: '\e940';
|
||||
}
|
||||
.icon-notes:before {
|
||||
content: '\e941';
|
||||
}
|
||||
.icon-lines:before {
|
||||
content: '\e942';
|
||||
}
|
||||
.icon-zone:before {
|
||||
content: '\e943';
|
||||
}
|
||||
.icon-greuge:before {
|
||||
content: '\e944';
|
||||
}
|
||||
.icon-ticketAdd:before {
|
||||
content: '\e945';
|
||||
}
|
||||
.icon-components:before {
|
||||
content: '\e946';
|
||||
}
|
||||
.icon-pets:before {
|
||||
content: '\e947';
|
||||
}
|
||||
.icon-linesprepaired:before {
|
||||
content: '\e948';
|
||||
}
|
||||
.icon-control:before {
|
||||
content: '\e949';
|
||||
}
|
||||
.icon-revision:before {
|
||||
content: '\e94a';
|
||||
}
|
||||
.icon-deaulter:before {
|
||||
content: '\e94b';
|
||||
}
|
||||
.icon-services:before {
|
||||
content: '\e94c';
|
||||
}
|
||||
.icon-albaran:before {
|
||||
content: '\e94d';
|
||||
}
|
||||
.icon-solunion:before {
|
||||
content: '\e94e';
|
||||
}
|
||||
.icon-stowaway:before {
|
||||
content: '\e94f';
|
||||
}
|
||||
.icon-apps:before {
|
||||
content: '\e951';
|
||||
}
|
||||
.icon-info:before {
|
||||
content: '\e952';
|
||||
}
|
||||
.icon-columndelete:before {
|
||||
content: '\e953';
|
||||
}
|
||||
.icon-columnadd:before {
|
||||
content: '\e954';
|
||||
}
|
||||
.icon-deleteline:before {
|
||||
content: '\e955';
|
||||
}
|
||||
.icon-item:before {
|
||||
content: '\e956';
|
||||
}
|
||||
.icon-worker:before {
|
||||
content: '\e957';
|
||||
}
|
||||
.icon-headercol:before {
|
||||
content: '\e958';
|
||||
}
|
||||
.icon-reserva:before {
|
||||
content: '\e959';
|
||||
}
|
||||
.icon-100:before {
|
||||
content: '\e95a';
|
||||
}
|
||||
.icon-sign:before {
|
||||
content: '\e95d';
|
||||
}
|
||||
.icon-polizon:before {
|
||||
content: '\e95e';
|
||||
}
|
||||
.icon-solclaim:before {
|
||||
content: '\e95f';
|
||||
}
|
||||
.icon-actions:before {
|
||||
content: '\e960';
|
||||
}
|
||||
.icon-details:before {
|
||||
content: '\e961';
|
||||
}
|
||||
.icon-traceability:before {
|
||||
content: '\e962';
|
||||
}
|
||||
.icon-claims:before {
|
||||
content: '\e963';
|
||||
}
|
||||
.icon-regentry:before {
|
||||
content: '\e964';
|
||||
}
|
||||
.icon-transaction:before {
|
||||
content: '\e966';
|
||||
}
|
||||
.icon-History:before {
|
||||
content: '\e968';
|
||||
}
|
||||
.icon-mana:before {
|
||||
content: '\e96a';
|
||||
}
|
||||
.icon-ticket:before {
|
||||
content: '\e96b';
|
||||
}
|
||||
.icon-niche:before {
|
||||
content: '\e96c';
|
||||
}
|
||||
.icon-tags:before {
|
||||
content: '\e96d';
|
||||
}
|
||||
.icon-volume:before {
|
||||
content: '\e96e';
|
||||
}
|
||||
.icon-bin:before {
|
||||
content: '\e96f';
|
||||
}
|
||||
.icon-splur:before {
|
||||
content: '\e970';
|
||||
}
|
||||
.icon-barcode:before {
|
||||
content: '\e971';
|
||||
}
|
||||
.icon-botanical:before {
|
||||
content: '\e972';
|
||||
}
|
||||
.icon-clone:before {
|
||||
content: '\e973';
|
||||
}
|
||||
.icon-sms:before {
|
||||
content: '\e975';
|
||||
}
|
||||
.icon-eye:before {
|
||||
content: '\e976';
|
||||
}
|
||||
.icon-doc:before {
|
||||
content: '\e977';
|
||||
}
|
||||
.icon-package:before {
|
||||
content: '\e978';
|
||||
}
|
||||
.icon-settings:before {
|
||||
content: '\e979';
|
||||
}
|
||||
.icon-bucket:before {
|
||||
content: '\e97a';
|
||||
}
|
||||
.icon-mandatory:before {
|
||||
content: '\e97b';
|
||||
}
|
||||
.icon-recovery:before {
|
||||
content: '\e97c';
|
||||
}
|
||||
.icon-payment:before {
|
||||
content: '\e97e';
|
||||
}
|
||||
.icon-grid:before {
|
||||
content: '\e980';
|
||||
}
|
||||
.icon-web:before {
|
||||
content: '\e982';
|
||||
}
|
||||
.icon-dfiscales:before {
|
||||
content: '\e984';
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Quasar SCSS (& Sass) Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
|
||||
|
||||
// Check documentation for full list of Quasar variables
|
||||
|
||||
// Your own variables (that are declared here) and Quasar's own
|
||||
// ones will be available out of the box in your .vue/.scss/.sass files
|
||||
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||
|
||||
$primary: #1976d2;
|
||||
$secondary: #26a69a;
|
||||
$accent: #9c27b0;
|
||||
|
||||
$dark: #1d1d1d;
|
||||
|
||||
$positive: #21ba45;
|
||||
$negative: #c10015;
|
||||
$info: #31ccec;
|
||||
$warning: #f2c037;
|
|
@ -1,7 +0,0 @@
|
|||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NODE_ENV: string;
|
||||
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
|
||||
VUE_ROUTER_BASE: string | undefined;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import toLowerCase from './toLowerCase';
|
||||
|
||||
export default {
|
||||
toLowerCase,
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
export default function toLowerCase(value: string): string {
|
||||
return value.toLowerCase();
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
export default {
|
||||
globals: {
|
||||
lang: {
|
||||
es: 'Spanish',
|
||||
en: 'English',
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
statusUnauthorized: 'Access denied',
|
||||
statusInternalServerError: 'An internal server error has ocurred',
|
||||
},
|
||||
login: {
|
||||
title: 'Login',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
submit: 'Log in',
|
||||
keepLogin: 'Keep me logged in',
|
||||
loginSuccess: 'You have successfully logged in',
|
||||
loginError: 'Invalid username or password',
|
||||
},
|
||||
customer: {},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
settings: 'Settings',
|
||||
logOut: 'Log Out',
|
||||
},
|
||||
},
|
||||
pages: {
|
||||
logIn: 'Log In',
|
||||
dashboard: 'Dashboard',
|
||||
customers: 'Customers',
|
||||
list: 'List',
|
||||
},
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
export default {
|
||||
globals: {
|
||||
lang: {
|
||||
es: 'Español',
|
||||
en: 'Inglés',
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
statusUnauthorized: 'Acceso denegado',
|
||||
statusInternalServerError: 'Ha ocurrido un error interno del servidor',
|
||||
},
|
||||
login: {
|
||||
title: 'Iniciar sesión',
|
||||
username: 'Nombre de usuario',
|
||||
password: 'Contraseña',
|
||||
submit: 'Iniciar sesión',
|
||||
keepLogin: 'Mantener sesión iniciada',
|
||||
loginSuccess: 'Inicio de sesión correcto',
|
||||
loginError: 'Nombre de usuario o contraseña incorrectos',
|
||||
},
|
||||
customer: {},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
settings: 'Configuración',
|
||||
logOut: 'Cerrar sesión',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
import en from './en';
|
||||
import es from './es';
|
||||
|
||||
export default {
|
||||
en: en,
|
||||
es: es,
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= productName %></title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="description" content="<%= productDescription %>" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="msapplication-tap-highlight" content="no" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
|
||||
/>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png" />
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png" />
|
||||
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- DO NOT touch the following DIV -->
|
||||
<div id="q-app"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,79 +0,0 @@
|
|||
<template>
|
||||
<q-layout view="hHh lpR fFf">
|
||||
<Navbar @toggle-drawer="onToggleDrawer()" />
|
||||
<q-drawer
|
||||
v-model="drawer"
|
||||
show-if-above
|
||||
:mini="miniState"
|
||||
@mouseover="miniState = false"
|
||||
@mouseout="miniState = true"
|
||||
mini-to-overlay
|
||||
:width="200"
|
||||
:breakpoint="500"
|
||||
>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<q-list padding>
|
||||
<q-item clickable v-ripple :to="{ path: '/dashboard' }" active-class="text-orange">
|
||||
<q-item-section avatar>
|
||||
<q-icon name="dashboard" />
|
||||
</q-item-section>
|
||||
<q-item-section>Dashboard</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-ripple :to="{ path: '/customer' }" active-class="text-orange">
|
||||
<q-item-section avatar>
|
||||
<q-icon name="people" />
|
||||
</q-item-section>
|
||||
<q-item-section>Customers</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-ripple :to="{ path: '/ticket' }" active-class="text-orange">
|
||||
<q-item-section avatar>
|
||||
<q-icon name="vn:ticket" />
|
||||
</q-item-section>
|
||||
<q-item-section>Tickets</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-ripple>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="receipt" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>Invoice Out</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-item clickable v-ripple>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="shopping_cart" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>Catalog</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-separator />
|
||||
|
||||
<q-item clickable v-ripple>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="drafts" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>Drafts</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view></router-view>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import Navbar from 'src/components/Navbar.vue';
|
||||
const drawer = ref(false);
|
||||
const miniState = ref(true);
|
||||
|
||||
function onToggleDrawer(): void {
|
||||
drawer.value = !drawer.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -1,15 +0,0 @@
|
|||
<template>
|
||||
<div class="row fit fixed">
|
||||
<div class="col-2">
|
||||
<q-card square>
|
||||
<router-link :to="{ path: '/customer/list' }">
|
||||
<q-icon name="arrow_back" size="md" color="primary" />
|
||||
</router-link>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<q-card>asd</q-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,3 +0,0 @@
|
|||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
|
@ -1,145 +0,0 @@
|
|||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<div class="column items-center q-gutter-y-md">
|
||||
<q-card v-for="customer in customers" :key="customer.id" class="card">
|
||||
<!-- v-ripple :to="{ path: '/dashboard' }" -->
|
||||
<q-item v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
||||
<q-item-section class="q-pa-md">
|
||||
<div class="text-h6">{{ customer.name }}</div>
|
||||
<q-item-label caption>@{{ customer.username }}</q-item-label>
|
||||
<div class="q-mt-md">
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>Email</q-item-label>
|
||||
<q-item-label>{{ customer.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>Phone</q-item-label>
|
||||
<q-item-label>{{ customer.phone }} </q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
</q-item-section>
|
||||
<q-btn color="grey-7" round flat icon="more_vert">
|
||||
<q-menu cover auto-close>
|
||||
<q-list>
|
||||
<q-item clickable>
|
||||
<q-item-section>Action 1</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable>
|
||||
<q-item-section>Action 2</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn flat round color="orange" icon="arrow_circle_right" @click="navigate(customer.id)" />
|
||||
<q-btn flat round color="accent" icon="preview" />
|
||||
<q-btn flat round color="accent" icon="vn:ticket" />
|
||||
<q-card-actions>
|
||||
<q-btn
|
||||
color="grey"
|
||||
round
|
||||
flat
|
||||
dense
|
||||
:icon="customer.expanded.value ? 'keyboard_arrow_up' : 'keyboard_arrow_down'"
|
||||
@click="customer.expanded.value = !customer.expanded.value"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
<q-slide-transition>
|
||||
<div v-show="customer.expanded.value">
|
||||
<q-separator />
|
||||
<q-card-section class="text-subitle2">
|
||||
<q-list>
|
||||
<q-item clickable>
|
||||
<q-item-section>
|
||||
<q-item-label>Address</q-item-label>
|
||||
<q-item-label caption>Avenue 11</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-card-section>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
<!-- <q-card-section horizontal>
|
||||
<q-card-section vertical class="col">
|
||||
<div class="text-h6">{{ customer.name }}</div>
|
||||
<div class="text-subtitle2">@{{ customer.username }}</div>
|
||||
|
||||
<q-card-section vertical>text</q-card-section>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator vertical />
|
||||
|
||||
<q-card-actions vertical class="justify-around">
|
||||
<q-btn flat round color="red" icon="favorite" />
|
||||
<q-btn flat round color="accent" icon="bookmark" />
|
||||
<q-btn color="grey-7" round flat icon="more_vert">
|
||||
<q-menu cover auto-close>
|
||||
<q-list>
|
||||
<q-item clickable>
|
||||
<q-item-section>Action 1</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable>
|
||||
<q-item-section>Action 2</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-card-section> -->
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, Ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
|
||||
interface Customer {
|
||||
id: number;
|
||||
name: string;
|
||||
username: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
expanded: Ref<boolean>;
|
||||
}
|
||||
const customers: Customer[] = [
|
||||
{
|
||||
id: 1101,
|
||||
name: 'Bruce Wayne',
|
||||
username: 'batman',
|
||||
email: 'batman@gotham',
|
||||
phone: '555-555-5555',
|
||||
expanded: ref(false),
|
||||
},
|
||||
{
|
||||
id: 1102,
|
||||
name: 'James Gordon',
|
||||
username: 'jamesgordon',
|
||||
email: 'jamesgordon@gotham',
|
||||
phone: '555-555-1111',
|
||||
expanded: ref(false),
|
||||
},
|
||||
];
|
||||
|
||||
function navigate(id: number) {
|
||||
router.push({ path: `/customer/${id}` });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
|
@ -1,84 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
const slide = ref('style');
|
||||
const slideText = 'Description text';
|
||||
</script>
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<q-banner
|
||||
v-if="$q.screen.gt.xs"
|
||||
inline-actions
|
||||
rounded
|
||||
class="bg-orange text-white q-mb-lg"
|
||||
>
|
||||
You have lost connection to the internet. This app is offline.
|
||||
<template #action>
|
||||
<q-btn flat label="Turn ON Wifi" />
|
||||
<q-btn flat label="Dismiss" />
|
||||
</template>
|
||||
</q-banner>
|
||||
|
||||
<div class="row items-start wrap q-col-gutter-md q-mb-lg">
|
||||
<div class="col-12 col-md">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-carousel
|
||||
v-model="slide"
|
||||
transition-prev="scale"
|
||||
transition-next="scale"
|
||||
swipeable
|
||||
animated
|
||||
control-color="white"
|
||||
navigation
|
||||
padding
|
||||
arrows
|
||||
height="300px"
|
||||
class="bg-orange-3 text-white shadow-1 rounded-borders"
|
||||
>
|
||||
<q-carousel-slide name="style" class="column no-wrap flex-center">
|
||||
<q-icon name="style" size="56px" />
|
||||
<div class="q-mt-md text-center">{{ slideText }}</div>
|
||||
</q-carousel-slide>
|
||||
<q-carousel-slide name="tv" class="column no-wrap flex-center">
|
||||
<q-icon name="live_tv" size="56px" />
|
||||
<div class="q-mt-md text-center">{{ slideText }}</div>
|
||||
</q-carousel-slide>
|
||||
<q-carousel-slide name="layers" class="column no-wrap flex-center">
|
||||
<q-icon name="layers" size="56px" />
|
||||
<div class="q-mt-md text-center">{{ slideText }}</div>
|
||||
</q-carousel-slide>
|
||||
<q-carousel-slide name="map" class="column no-wrap flex-center">
|
||||
<q-icon name="terrain" size="56px" />
|
||||
<div class="q-mt-md text-center">{{ slideText }}</div>
|
||||
</q-carousel-slide>
|
||||
</q-carousel>
|
||||
</div>
|
||||
<div class="col-12 col-md">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-card class="q-pa-md">Dashboard page..</q-card>
|
||||
</div>
|
||||
<div class="col-12 col-md">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-card class="q-pa-md">Dashboard page..</q-card>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row items-start q-col-gutter-md q-mb-lg">
|
||||
<div class="col-12 col-md">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-card class="q-pa-md">Dashboard page..</q-card>
|
||||
</div>
|
||||
<div class="col-12 col-md">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-card class="q-pa-md">Dashboard page..</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row items-start q-col-gutter-md q-mb-lg">
|
||||
<div class="col">
|
||||
<div class="text-h6 text-grey-8 q-mb-sm">Responsive monitor</div>
|
||||
<q-card class="q-pa-md">Dashboard page..</q-card>
|
||||
</div>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -1,147 +0,0 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<q-header class="transparent">
|
||||
<q-toolbar>
|
||||
<q-space></q-space>
|
||||
<q-toggle
|
||||
:label="t(`globals.lang['${locale}']`)"
|
||||
icon="public"
|
||||
color="orange"
|
||||
false-value="es"
|
||||
true-value="en"
|
||||
v-model="locale"
|
||||
/>
|
||||
<q-toggle
|
||||
v-model="darkMode"
|
||||
checked-icon="dark_mode"
|
||||
color="orange"
|
||||
unchecked-icon="light_mode"
|
||||
/>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
<q-page-container>
|
||||
<q-page>
|
||||
<div id="login">
|
||||
<q-card class="login q-pa-xl">
|
||||
<q-img
|
||||
src="~/assets/logo.svg"
|
||||
alt="Logo"
|
||||
fit="contain"
|
||||
:ratio="16 / 9"
|
||||
class="q-mb-md"
|
||||
/>
|
||||
<q-form @submit="onSubmit" class="q-gutter-md">
|
||||
<q-input
|
||||
filled
|
||||
color="orange"
|
||||
v-model="username"
|
||||
:label="t('login.username')"
|
||||
lazy-rules
|
||||
:rules="[
|
||||
(val) => (val && val.length > 0) || 'Please type something',
|
||||
]"
|
||||
/>
|
||||
<q-input
|
||||
filled
|
||||
color="orange"
|
||||
type="password"
|
||||
v-model="password"
|
||||
:label="t('login.password')"
|
||||
lazy-rules
|
||||
:rules="[
|
||||
(val) => (val && val.length > 0) || 'Please type something',
|
||||
]"
|
||||
/>
|
||||
<q-toggle
|
||||
v-model="keepLogin"
|
||||
:label="t('login.keepLogin')"
|
||||
color="orange"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<q-btn :label="t('login.submit')" type="submit" color="orange" />
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { Dark, useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const session = useSession();
|
||||
const router = useRouter();
|
||||
const { t, locale } = useI18n();
|
||||
|
||||
let username = ref('');
|
||||
let password = ref('');
|
||||
let keepLogin = ref(true);
|
||||
|
||||
const darkMode = computed({
|
||||
get(): boolean {
|
||||
return Dark.isActive;
|
||||
},
|
||||
set(value: boolean): void {
|
||||
Dark.set(value);
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(): Promise<void> {
|
||||
try {
|
||||
const { data } = await axios.post('/api/accounts/login', {
|
||||
user: username.value,
|
||||
password: password.value,
|
||||
});
|
||||
|
||||
session.setToken({
|
||||
token: data.token,
|
||||
keepLogin: keepLogin.value,
|
||||
});
|
||||
|
||||
quasar.notify({
|
||||
message: t('login.loginSuccess'),
|
||||
type: 'positive',
|
||||
});
|
||||
|
||||
await router.push({ path: '/dashboard' });
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const errorCode = error.response && error.response.status;
|
||||
if (errorCode === 401) {
|
||||
quasar.notify({
|
||||
message: t('login.loginError'),
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
quasar.notify({
|
||||
message: t('errors.statusInternalServerError'),
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
.login {
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
|
@ -1,66 +0,0 @@
|
|||
import { jest, describe, expect, it } from '@jest/globals';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
import { Notify } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import Login from '../Login.vue';
|
||||
|
||||
// Specify here Quasar config you'll need to test your component
|
||||
installQuasarPlugin({
|
||||
plugins: {
|
||||
Notify
|
||||
}
|
||||
});
|
||||
const routerPushMock = jest.fn();
|
||||
|
||||
jest.mock('vue-router', () => ({
|
||||
useRouter: () => ({
|
||||
push: routerPushMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
|
||||
describe('Login', () => {
|
||||
it('should successfully set the token into session', async () => {
|
||||
const wrapper = mount(Login, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
|
||||
jest.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
|
||||
jest.spyOn(vm.quasar, 'notify')
|
||||
|
||||
expect(vm.session.getToken()).toEqual('');
|
||||
|
||||
await vm.onSubmit();
|
||||
|
||||
expect(vm.session.getToken()).toEqual('token');
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining(
|
||||
{ 'type': 'positive' }
|
||||
));
|
||||
vm.session.destroy();
|
||||
});
|
||||
|
||||
it('should not set the token into session if any error occurred', async () => {
|
||||
const wrapper = mount(Login, {
|
||||
global: {
|
||||
plugins: [i18n]
|
||||
}
|
||||
});
|
||||
const { vm } = wrapper;
|
||||
|
||||
jest.spyOn(axios, 'post').mockRejectedValue(new Error('error'));
|
||||
jest.spyOn(vm.quasar, 'notify')
|
||||
|
||||
expect(vm.session.getToken()).toEqual('');
|
||||
|
||||
await vm.onSubmit();
|
||||
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining(
|
||||
{ 'type': 'negative' }
|
||||
));
|
||||
});
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
<template>
|
||||
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div style="font-size: 30vh">404</div>
|
||||
|
||||
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div>
|
||||
|
||||
<q-btn class="q-mt-xl" color="white" text-color="blue" unelevated to="/" label="Go Home" no-caps />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Error404',
|
||||
});
|
||||
</script>
|
|
@ -1,7 +0,0 @@
|
|||
// Forces TS to apply `@quasar/app` augmentations of `quasar` package
|
||||
// Removing this would break `quasar/wrappers` imports as those typings are declared
|
||||
// into `@quasar/app`
|
||||
// As a side effect, since `@quasar/app` reference `quasar` to augment it,
|
||||
// this declaration also apply `quasar` own
|
||||
// augmentations (eg. adds `$q` into Vue component context)
|
||||
/// <reference types="@quasar/app" />
|
|
@ -1,71 +0,0 @@
|
|||
import { route } from 'quasar/wrappers';
|
||||
import { createMemoryHistory, createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
||||
import routes from './routes';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
const session = useSession();
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
*
|
||||
* The function below can be async too; either use
|
||||
* async/await or return a Promise which resolves
|
||||
* with the Router instance.
|
||||
*/
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
Router.beforeEach((to, from, next) => {
|
||||
const { isLoggedIn } = session;
|
||||
|
||||
if (!isLoggedIn && to.name !== 'Login') {
|
||||
next({ path: '/login', query: { redirect: to.fullPath } });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
Router.afterEach((to) => {
|
||||
interface Meta {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const { t } = i18n.global;
|
||||
let title = '';
|
||||
|
||||
const parent = to.matched[1];
|
||||
if (parent) {
|
||||
const parentMeta: Meta = parent.meta;
|
||||
if (parentMeta && parentMeta.title) {
|
||||
title += t(`pages.${parentMeta.title}`);
|
||||
}
|
||||
}
|
||||
//const childTitle: string = childMeta.title;
|
||||
|
||||
const childMeta: Meta = to.meta;
|
||||
if (childMeta && childMeta.title) {
|
||||
if (title != '') title += ' - ';
|
||||
|
||||
title += t(`pages.${childMeta.title}`);
|
||||
}
|
||||
|
||||
document.title = title;
|
||||
});
|
||||
|
||||
return Router;
|
||||
});
|
|
@ -1,51 +0,0 @@
|
|||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
meta: { title: 'logIn' },
|
||||
component: () => import('../pages/Login/Login.vue'),
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'Main',
|
||||
component: () => import('../layouts/Main.vue'),
|
||||
redirect: { name: 'Dashboard' },
|
||||
children: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard' },
|
||||
component: () => import('../pages/Dashboard/Dashboard.vue'),
|
||||
},
|
||||
{
|
||||
path: '/customer',
|
||||
name: 'Customer',
|
||||
meta: { title: 'customers' },
|
||||
component: () => import('../pages/Customer/Customer.vue'),
|
||||
redirect: { name: 'List' },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'List',
|
||||
meta: { title: 'list' },
|
||||
component: () => import('../pages/Customer/List.vue'),
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
name: 'Card',
|
||||
component: () => import('../pages/Customer/Card/Card.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('../pages/NotFound.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
|
@ -1,7 +0,0 @@
|
|||
// Mocks all files ending in `.vue` showing them as plain Vue instances
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
videos/*
|
||||
screenshots/*
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/// <reference types="cypress" />
|
||||
// Use `cy.dataCy` custom command for more robust tests
|
||||
// See https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements
|
||||
|
||||
// ** This file is an example of how to write Cypress tests, you can safely delete it **
|
||||
|
||||
// This test will pass when run against a clean Quasar project
|
||||
describe('Landing', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/#/login');
|
||||
});
|
||||
it('should log in', () => {
|
||||
cy.get('input[aria-label="Username"]').type('developer')
|
||||
cy.get('input[aria-label="Password"]').type('nightmare')
|
||||
cy.get('button[type="submit"]').click()
|
||||
});
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
/// <reference types="cypress" />
|
||||
/* eslint-env node */
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
// cypress/plugins/index.js
|
||||
|
||||
const { injectDevServer } = require('@quasar/quasar-app-extension-testing-e2e-cypress/cct-dev-server');
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = async (on, config) => {
|
||||
// Enable component testing, you can safely remove this
|
||||
// if you don't plan to use Cypress for unit tests
|
||||
// if (config.testingType === 'component') {
|
||||
// await injectDevServer(on, config);
|
||||
// }
|
||||
|
||||
return config;
|
||||
};
|
|
@ -1,30 +0,0 @@
|
|||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
// DO NOT REMOVE
|
||||
// Imports Quasar Cypress AE predefined commands
|
||||
import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
||||
registerCommands();
|
|
@ -1,16 +0,0 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your e2e test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import './commands';
|
|
@ -1,46 +0,0 @@
|
|||
// ***********************************************************
|
||||
// This example support/unit.js is processed and
|
||||
// loaded automatically before your unit test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import './commands';
|
||||
|
||||
// Change this if you have a different entrypoint for the main scss.
|
||||
import 'src/css/app.scss';
|
||||
// Quasar styles
|
||||
import 'quasar/src/css/index.sass';
|
||||
|
||||
// ICON SETS
|
||||
// If you use multiple or different icon-sets then the default, be sure to import them here.
|
||||
import 'quasar/dist/icon-set/material-icons.umd.prod';
|
||||
import '@quasar/extras/material-icons/material-icons.css';
|
||||
|
||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-e2e-cypress';
|
||||
import { config } from '@vue/test-utils';
|
||||
import { Dialog } from 'quasar';
|
||||
|
||||
// Example to import i18n from boot and use as plugin
|
||||
// import { i18n } from 'src/boot/i18n';
|
||||
|
||||
// You can modify the global config here for all tests or pass in the configuration per test
|
||||
// For example use the actual i18n instance or mock it
|
||||
// config.global.plugins.push(i18n);
|
||||
config.global.mocks = {
|
||||
$t: () => '',
|
||||
};
|
||||
|
||||
// Overwrite the transition and transition-group stubs which are stubbed by test-utils by default.
|
||||
// We do want transitions to show when doing visual testing :)
|
||||
config.global.stubs = {};
|
||||
|
||||
installQuasarPlugin({ plugins: { Dialog } });
|
|
@ -1,26 +0,0 @@
|
|||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import { Dialog } from 'quasar';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DialogWrapper',
|
||||
props: {
|
||||
component: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
componentProps: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
Dialog.create({
|
||||
component: props.component,
|
||||
|
||||
// props forwarded to your custom component
|
||||
componentProps: props.componentProps,
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,20 +0,0 @@
|
|||
<template>
|
||||
<q-layout>
|
||||
<component :is="component" v-bind="$attrs" />
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutContainer',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
component: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -1,10 +0,0 @@
|
|||
module.exports = {
|
||||
extends: [
|
||||
// Removes 'no-undef' lint errors for Jest global functions (`describe`, `it`, etc),
|
||||
// add Jest-specific lint rules and Jest plugin
|
||||
// See https://github.com/jest-community/eslint-plugin-jest#recommended
|
||||
'plugin:jest/recommended',
|
||||
// Uncomment following line to apply style rules
|
||||
// 'plugin:jest/style',
|
||||
],
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
coverage/*
|
|
@ -1,41 +0,0 @@
|
|||
import { describe, expect, it } from '@jest/globals';
|
||||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
import { QBtn } from 'quasar';
|
||||
import MyButton from './demo/MyButton.vue';
|
||||
|
||||
// Specify here Quasar config you'll need to test your component
|
||||
installQuasarPlugin();
|
||||
|
||||
describe('MyButton', () => {
|
||||
it('has increment method', () => {
|
||||
const wrapper = mount(MyButton);
|
||||
const { vm } = wrapper;
|
||||
|
||||
expect(typeof vm.increment).toBe('function');
|
||||
});
|
||||
|
||||
it('can check the inner text content', () => {
|
||||
const wrapper = mount(MyButton);
|
||||
const { vm } = wrapper;
|
||||
|
||||
expect(vm.$el.textContent).toContain('rocket muffin');
|
||||
expect(wrapper.find('.content').text()).toContain('rocket muffin');
|
||||
});
|
||||
|
||||
it('sets the correct default data', () => {
|
||||
const wrapper = mount(MyButton);
|
||||
const { vm } = wrapper;
|
||||
|
||||
expect(typeof vm.counter).toBe('number');
|
||||
expect(vm.counter).toBe(0);
|
||||
});
|
||||
|
||||
it('correctly updates counter when button is pressed', async () => {
|
||||
const wrapper = shallowMount(MyButton);
|
||||
const { vm } = wrapper;
|
||||
const button = wrapper.findComponent(QBtn);
|
||||
await button.trigger('click');
|
||||
expect(vm.counter).toBe(1);
|
||||
});
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-jest';
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import MyDialog from './demo/MyDialog.vue';
|
||||
|
||||
installQuasarPlugin();
|
||||
|
||||
describe('MyDialog', () => {
|
||||
it('should mount MyDialog', () => {
|
||||
const wrapper = mount(MyDialog, {
|
||||
data: () => ({
|
||||
isDialogOpen: true,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
const props = defineProps({
|
||||
incrementStep: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const counter = ref(0);
|
||||
const input = ref('rocket muffin');
|
||||
function increment() {
|
||||
counter.value += props.incrementStep;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p class="content">{{ input }}</p>
|
||||
<span>{{ counter }}</span>
|
||||
<q-btn class="button" @click="increment()"></q-btn>
|
||||
</div>
|
||||
</template>
|
|
@ -1,12 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
const isDialogOpen = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog v-model="isDialogOpen">
|
||||
<q-card>
|
||||
<q-card-section>Custom dialog which should be tested</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"extends": "@quasar/app/tsconfig-preset",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue