Merge to master #21

Merged
joan merged 163 commits from test into master 2022-10-20 11:59:30 +00:00
78 changed files with 0 additions and 40148 deletions
Showing only changes of commit d470a3f676 - Show all commits

View File

@ -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"
}
}
]
]
}
}
}

View File

@ -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

View File

@ -1,9 +0,0 @@
/dist
/src-bex/www
/src-capacitor
/src-cordova
/.quasar
/node_modules
.eslintrc.js
babel.config.js
/src-ssr

View File

@ -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',
],
},
],
};

34
.gitignore vendored
View File

@ -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

View File

@ -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'),
],
};

View File

@ -1,7 +0,0 @@
{
"singleQuote": true,
"printWidth": 120,
"tabWidth": 4,
"semi": true,
"endOfLine": "auto"
}

View File

@ -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"]
}

View File

@ -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).

View File

@ -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,
};

View File

@ -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"
}
}

View File

@ -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'],
};

35066
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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
},
},
};
});

View File

@ -1,9 +0,0 @@
{
"@quasar/testing-unit-jest": {
"babel": "babelrc",
"options": ["scripts", "typescript"]
},
"@quasar/testing-e2e-cypress": {
"options": ["scripts"]
}
}

View File

@ -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"
}
}

View File

@ -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>

View File

@ -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="&#38;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

View File

@ -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>

View File

View File

@ -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 };

View File

@ -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 };

View File

@ -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>

View File

@ -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>

View File

@ -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();
});
});

View File

@ -1,8 +0,0 @@
export interface Todo {
id: number;
content: string;
}
export interface Meta {
totalCount: number;
}

View File

@ -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,
};
}
*/

View File

@ -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,
};
}

View File

@ -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,
};
}

View File

@ -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

View File

@ -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';
}

View File

@ -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;

7
src/env.d.ts vendored
View File

@ -1,7 +0,0 @@
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: string;
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
VUE_ROUTER_BASE: string | undefined;
}
}

View File

@ -1,5 +0,0 @@
import toLowerCase from './toLowerCase';
export default {
toLowerCase,
};

View File

@ -1,3 +0,0 @@
export default function toLowerCase(value: string): string {
return value.toLowerCase();
}

View File

@ -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',
},
};

View File

@ -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',
},
},
};

View File

@ -1,7 +0,0 @@
import en from './en';
import es from './es';
export default {
en: en,
es: es,
};

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,3 +0,0 @@
<template>
<router-view></router-view>
</template>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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' }
));
});
});

View File

@ -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>

7
src/quasar.d.ts vendored
View File

@ -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" />

View File

@ -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;
});

View File

@ -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;

7
src/shims-vue.d.ts vendored
View File

@ -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;
}

View File

@ -1,2 +0,0 @@
videos/*
screenshots/*

View File

@ -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"
}

View File

@ -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()
});
});

View File

@ -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;
};

View File

@ -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();

View File

@ -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';

View File

@ -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 } });

View File

@ -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>

View File

@ -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>

View File

@ -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',
],
};

View File

@ -1 +0,0 @@
coverage/*

View File

@ -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);
});
});

View File

@ -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);
});
});

View File

@ -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>

View File

@ -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>

View File

@ -1,6 +0,0 @@
{
"extends": "@quasar/app/tsconfig-preset",
"compilerOptions": {
"baseUrl": "."
}
}