Migrate from Vue to React framework.

This commit is contained in:
Ahmed Bouhuolia
2020-02-16 17:38:55 +02:00
parent a97b527e8c
commit b3849e55e9
125 changed files with 12278 additions and 16669 deletions

View File

@@ -1,29 +0,0 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": [
"transform-vue-jsx",
"transform-runtime",
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "~node_modules/element-theme-chalk/src",
"ext": ".scss"
}
]
],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "istanbul"],
}
}
}

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,7 +0,0 @@
/build/
/config/
/dist/
/*.js
{{#unit}}
/test/unit/coverage/
{{/unit}}

View File

@@ -1,53 +0,0 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
extends: ['plugin:vue/essential', 'airbnb-base'],
// required to lint *.vue files
plugins: [
'vue'
],
// check if imports actually resolve
settings: {
'import/resolver': {
webpack: {
config: 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here
rules: {
// don't require .vue extension when importing
'import/extensions': ['error', 'always', {
js: 'never',
vue: 'never'
}],
// disallow reassignment of function parameters
// disallow parameter object manipulation except for specific exclusions
'no-param-reassign': ['error', {
props: true,
ignorePropertyModificationsFor: [
'state', // for vuex state
's',
'acc', // for reduce accumulators
'e' // for e.returnvalue
]
}],
// allow optionalDependencies
'import/no-extraneous-dependencies': ['error', {
optionalDependencies: ['test/unit/index.js']
}],
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

31
client/.gitignore vendored
View File

@@ -1,16 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
node_modules/
/dist/
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/
/test/e2e/reports/
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

View File

@@ -1,10 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

View File

@@ -1,36 +1,68 @@
# {{ name }}
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
> {{ description }}
## Available Scripts
## Build Setup
In the project directory, you can run:
``` bash
# install dependencies
npm install
### `npm start`
# serve with hot reload at localhost:8080
npm run dev
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
# build for production with minification
npm run build
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
# build for production and view the bundle analyzer report
npm run build --report
{{#unit}}
### `npm test`
# run unit tests
npm run unit
{{/unit}}
{{#e2e}}
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
# run e2e tests
npm run e2e
{{/e2e}}
{{#if_or unit e2e}}
### `npm run build`
# run all tests
npm test
{{/if_or}}
```
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

View File

@@ -1,41 +0,0 @@
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // if you are using ts-loader, setting this to true will make typescript errors show up during build
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@@ -1,54 +0,0 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,101 +0,0 @@
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

View File

@@ -1,22 +0,0 @@
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

View File

@@ -1,91 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

View File

@@ -1,95 +0,0 @@
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

View File

@@ -1,149 +0,0 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: process.env.NODE_ENV === 'testing'
? 'index.html'
: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@@ -1,32 +0,0 @@
'use strict'
// This is the webpack config used for unit tests.
const utils = require('./utils')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const webpackConfig = merge(baseWebpackConfig, {
// use inline sourcemap for karma-sourcemap-loader
module: {
rules: utils.styleLoaders()
},
devtool: '#inline-source-map',
resolveLoader: {
alias: {
// necessary to to make lang="scss" work in test when using vue-loader's ?inject option
// see discussion at https://github.com/vuejs/vue-loader/issues/724
'scss-loader': 'sass-loader'
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/test.env')
})
]
})
// no need for app entry during tests
delete webpackConfig.entry
module.exports = webpackConfig

View File

@@ -1,7 +0,0 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})

View File

@@ -1,74 +0,0 @@
'use strict'
// Template version: {{ template_version }}
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: false,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

View File

@@ -1,4 +0,0 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}

View File

@@ -1,7 +0,0 @@
'use strict'
const merge = require('webpack-merge')
const devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})

View File

@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>{{ name }}</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

7
client/jsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}

21656
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,105 +1,45 @@
{
"name": "Ratteb",
"version": "1.0.0",
"description": "Ratteb, sales and accounting software",
"author": "Ahmed Bouhuolia <a.bouhuolia@gmail.com>",
"name": "client",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
"e2e": "node test/e2e/runner.js",
"test": "{{#unit}}npm run unit{{/unit}}{{#unit}}{{#e2e}} && {{/e2e}}{{/unit}}{{#e2e}}npm run e2e{{/e2e}}",
"lint": "eslint --ext .js,.vue src{{#unit}} test/unit{{/unit}}{{#e2e}} test/e2e/specs{{/e2e}}",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.19.0",
"element-ui": "^2.11.1",
"vue": "^2.5.2",
"vue-axios": "^2.1.4",
"vue-router": "^3.0.1",
"vuex": "^3.1.1",
"vuex-i18n": "^1.13.1",
"element-theme-chalk": "^2.12.0"
"@blueprintjs/core": "^3.23.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-hook-form": "^4.9.4",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"yup": "^0.28.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^7.2.3",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-component": "^1.1.1",
"babel-plugin-istanbul": "^4.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chai": "^4.1.2",
"chalk": "^2.0.1",
"chromedriver": "^2.27.2",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^5.0.1",
"cross-spawn": "^5.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-airbnb-base": "^11.3.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"inject-loader": "^3.0.0",
"karma": "^1.4.1",
"karma-coverage": "^1.1.1",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-phantomjs-shim": "^1.4.0",
"karma-sinon-chai": "^1.3.1",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.31",
"karma-webpack": "^2.0.2",
"mocha": "^3.2.0",
"nightwatch": "^0.9.12",
"node-notifier": "^5.1.2",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"phantomjs-prebuilt": "^2.1.14",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"sass-loader": "^7.2.0",
"selenium-server": "^3.0.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"sinon": "^4.0.0",
"sinon-chai": "^2.8.0",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
"redux-devtools": "^3.5.0"
}
}

BIN
client/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

43
client/public/index.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
client/public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
client/public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
client/public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -1,19 +0,0 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style lang="scss">
@import 'style/style.scss';
body{
margin: 0;
}
</style>

View File

@@ -0,0 +1,2 @@
export const FETCH_ACCOUNTS_CHART = 'FETCH_ACCOUNTS_CHART';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import dashboardRoutes from 'routes/dashboard'
export default function() {
return (
<Switch>
{ dashboardRoutes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
)
}

View File

@@ -1,113 +0,0 @@
<template>
<div class="hello">
<h1>\{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li>
<a
href="https://vuejs.org"
target="_blank"
>
Core Docs
</a>
</li>
<li>
<a
href="https://forum.vuejs.org"
target="_blank"
>
Forum
</a>
</li>
<li>
<a
href="https://chat.vuejs.org"
target="_blank"
>
Community Chat
</a>
</li>
<li>
<a
href="https://twitter.com/vuejs"
target="_blank"
>
Twitter
</a>
</li>
<br>
<li>
<a
href="http://vuejs-templates.github.io/webpack/"
target="_blank"
>
Docs for This Template
</a>
</li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li>
<a
href="http://router.vuejs.org/"
target="_blank"
>
vue-router
</a>
</li>
<li>
<a
href="http://vuex.vuejs.org/"
target="_blank"
>
vuex
</a>
</li>
<li>
<a
href="http://vue-loader.vuejs.org/"
target="_blank"
>
vue-loader
</a>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
>
awesome-vue
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
msg: 'Welcome to Your Vue.js App',
};
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,9 @@
export default function() {
return (
<div class="sidebar" id="sidebar">
</div>
)
};

View File

@@ -0,0 +1,19 @@
import React from 'react';
export default function() {
return (
<div class="sidebar__head">
<div class="sidebar__head-logo">
</div>
<div class="sidebar__head-company-meta">
<span class="comapny-name">
</span>
<span class="company-meta"></span>
</div>
</div>
);
};

View File

@@ -0,0 +1,30 @@
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
} from 'react-router-dom';
import SidebarContainer from './SidebarContainer';
import SidebarHead from './SidebarHead';
import sidebarRoutes from '../../routes/sidebar';
export default function Sidebar() {
return (
<SidebarContainer>
<SidebarHead />
<div className="sidebar__menu">
<Menu>
{ sidebarRoutes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
children={<route.sidebar />}
/>
))}
</Menu>
</div>
</SidebarContainer>
)
}

View File

@@ -1,58 +0,0 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true,
},
className: {
type: String,
default: '',
},
},
computed: {
isExternal() {
return Boolean(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`;
},
svgClass() {
if (this.className) {
return `svg-icon ${this.className}`;
}
return 'svg-icon';
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>

View File

@@ -0,0 +1,17 @@
const round = number => Math.round(number * 100) / 100
const monitorReducerEnhancer = createStore => (
reducer,
initialState,
enhancer
) => {
const monitoredReducer = (state, action) => {
const start = performance.now()
const newState = reducer(state, action)
const end = performance.now()
const diff = round(end - start)
console.log('reducer process time:', diff)
return newState
}
return createStore(monitoredReducer, initialState, enhancer)
}
export default monitorReducerEnhancer

30
client/src/index.js Normal file
View File

@@ -0,0 +1,30 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
import thunkMiddleware from 'redux-thunk';
import monitorReducerEnhancer from './enhancers/monitorReducer';
// import axios from 'axios';
import App from './pages/App';
import * as serviceWorker from './serviceWorker';
import loggerMiddleware from './middleware/logger'
const middlewareEnhancer = applyMiddleware(loggerMiddleware, thunkMiddleware)
const composedEnhancers = compose(
middlewareEnhancer,
monitorReducerEnhancer,
)
const store = createStore(rootReducer, undefined, composedEnhancers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

View File

@@ -1,22 +0,0 @@
export default {
login: 'Login',
password: 'Password',
username_password: 'Username or Email',
with_your_account: 'With your Ratteb account.',
if_you_forget_your_password: 'If you forgot your password, well, enter your Moosher ID than secret key to reset your password.',
sign_in: 'Sign in',
dashboard: 'Dashboard',
reset_the_password: 'Reset the Password',
enter_your_new_password: 'Enter You New Password',
reset_password_link_is_invalid: 'The link is invalid or expired. Please reset your password again.',
your_password_has_been_changed: 'Success Your password has been changed.',
username_password_do_not_match: 'The username and password you entered did not match our records.',
the_account_suspended_please_contact: 'This account is suspended, Please contact the administrator.',
email_has_been_sent: 'An e-mail has been sent to {email} with further instructions.',
there_is_no_email_with_that_address: 'There is no email with that address, Please try again.',
reset_your_password: 'Reset Your Password',
return_to_login: 'Return to Login',
forget_password: 'Forget your password?',
reset_password: 'Reset Password',
email_password_do_not_match: 'Email and password do not match.',
};

7
client/src/logo.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,28 +0,0 @@
import Vue from 'vue';
import {Form, FormItem, Input, Tabs, TabPane, Button, Alert} from 'element-ui';
import App from '@/App';
import router from '@/router';
import store from '@/store';
import '@/plugins/icons';
// Plugins
import '@/plugins/i18n';
Vue.config.productionTip = false;
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Tabs);
Vue.use(TabPane);
Vue.use(Button);
Vue.use(Alert);
const app = new Vue({
el: '#app',
render: h => h(App),
router,
store,
});
export default app;

View File

@@ -0,0 +1,9 @@
const logger = store => next => action => {
console.group(action.type)
console.info('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
console.groupEnd()
return result
}
export default logger

View File

@@ -1,20 +0,0 @@
import Vue from 'vue';
export default Vue.extend({
name: 'reloadable',
created() {
this.$eventBus.$on('reload-data', this.onReloadData);
},
methods: {
async onReloadData() {
this.$nprogress.start();
await this.reloadData();
this.$nprogress.done();
},
reloadData() {},
},
beforeDestroy() {
this.$eventBus.$off('reload-data', this.onReloadData);
},
});

12
client/src/pages/App.js Normal file
View File

@@ -0,0 +1,12 @@
import React from 'react';
import Authentication from 'pages/Authentication';
function App() {
return (
<div className="App">
<h1>Hello World</h1>
<Authentication />
</div>
);
}
export default App;

View File

@@ -1,207 +0,0 @@
<template>
<div class="page-auth page-auth--login">
<div class="page-auth__content-side">
<div class="page-auth__content-header">
</div>
<div class="page-auth__content-form">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</div>
<AuthFooter></AuthFooter>
</div>
<AuthMedia></AuthMedia>
</div>
</template>
<script>
import AuthMedia from '@/pages/Auth/AuthMedia.vue';
import AuthFooter from '@/pages/Auth/AuthFooter.vue';
export default {
name: 'auth-container',
components: {
AuthMedia,
AuthFooter,
},
};
</script>
<style lang="scss">
.page-auth{
display: flex;
min-height: 100vh;
&__content-side{
background: #fff;
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 100%;
// @include media-breakpoint-up('lg'){
width: 62%;
// }
.remember-forget-wrapper{
display: flex;
justify-content: space-between;
padding-bottom: 20px;
padding-top: 5px;
}
.form-note{
margin-top: 25px;
}
.btn-primary{
padding-top: 7px;
padding-bottom: 7px;
font-weight: 600;
font-size: 15px;
}
}
&__content{
&-header{
max-width: 440px;
width: 100%;
padding: 0 30px;
margin: 0 auto;
padding-top: 40px;
padding-bottom: 10px;
// @include media-breakpoint-down('xs'){
// padding-top: 25px;
// padding-left: 25px;
// padding-right: 25px;
// }
.icon-logo{
// @include media-breakpoint-down('xs'){
// width: 140px;
// }
}
}
&-title{
font-size: 42px;
font-weight: 300;
letter-spacing: -0.025em;
color: #253992;
line-height: 1.2;
padding-bottom: 23px;
font-size: 2.4em;
// @include media-breakpoint-down('xs'){
// font-size: 2.2rem;
// }
small{
font-size: 0.46em;
display: block;
font-weight: 400;
color: #495463;
letter-spacing: normal;
margin-top: 10px;
line-height: 1.6;
}
.text-success{
color: #00d285;
font-size: 0.41em;
font-weight: 400;
}
}
&-form{
width: 440px;
max-width: 100%;
margin-left: auto;
margin-right: auto;
padding: 40px 30px 50px;
// @include media-breakpoint-down('xs'){
// padding-left: 25px;
// padding-right: 25px;
// }
}
&-footer{
margin: 0 auto;
width: 440px;
max-width: 100%;
margin-left: auto;
margin-right: auto;
padding: 0 30px;
padding-bottom: 25px;
.footer-links{
margin: 0;
padding: 0;
list-style: none;
li{
padding: 2px 15px;
font-size: 13px;
color: #758698;
display: inline-block;
&:first-child{
padding-left: 0;
}
&:last-child{
padding-right: 0;
}
}
a{
text-decoration: none;
color: inherit;
&:hover{
color: #2c80ff;
}
}
}
}
}
/** Media Side **/
&__media-side{
display: flex;
width: 38%;
background-image: url('/images/cover.png');
background-repeat: no-repeat;
background-size: cover;
position: relative;
background-position: 50% 0;
// @include media-breakpoint-down('md'){
// width: 38%;
// }
// @include media-breakpoint-down('xs'){
// display: none;
// }
}
&__media-overlay{
position: absolute;
background: #202141;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.65;
}
}
</style>

View File

@@ -1,15 +0,0 @@
<template>
<div class="page-auth__content-footer">
<ul class="footer-links">
<li><a href="#">{{ $t('privacy_policy') }}</a></li>
<li><a href="#">{{ $t('terms_and_conditions') }}</a></li>
<li>© 2019 Moosher.</li>
</ul>
</div>
</template>
<script>
export default {
name: 'auth-footer',
}
</script>

View File

@@ -1,11 +0,0 @@
<template>
<div class="page-auth__media-side">
<div class="page-auth__media-overlay"></div>
</div>
</template>
<script>
export default {
name: 'auth-media',
};
</script>

View File

@@ -1,51 +0,0 @@
<template>
<div id="reset-password">
<h2 class="page-auth__content-title">
{{ $t('reset_your_password') }}
<small>{{ $t('if_you_forget_your_password') }}</small>
</h2>
<el-form ref="form" class="form-container">
<el-form-item :label="$t('username_password')" prop="title">
<el-input v-model="form.crediential" :maxlength="100" name="name" required />
</el-form-item>
</el-form>
<el-button type="primary">{{ $t('reset_password') }}</el-button>
<div class="form-note">
<router-link :to="{name: 'login'}">{{ $t('return_to_login') }}</router-link>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
name: 'forget-password',
data() {
return {
form: {
crediential: '',
},
};
},
methods: {
...mapActions(['login']),
/**
* Handle the submitting the reset password form.
*/
onSubmit() {
const form = {};
this.sendResetPassword({ form }).then(() => {
// console.log(response);
}).catch(() => {
// console.log(error);
});
},
},
};
</script>

View File

@@ -1,90 +0,0 @@
<template>
<div class="login">
<h2 class="page-auth__content-title">
{{ $t('sign_in') }}
<small>{{ $t('with_your_account') }}</small>
</h2>
<el-alert :active="isCredentialError" :type="'error'">
{{ $t('email_password_do_not_match') }}
</el-alert>
<el-alert :isActive="isInactiveError" :type="'error'">
{{ $t('the_account_suspended_please_contact') }}
</el-alert>
<el-alert :isActive="isResetPasswordSuccess" type="success">
{{ $t('your_password_has_been_changed') }}
</el-alert>
<el-form ref="form" class="form-container">
<el-form-item :label="$t('username_password')" prop="title">
<el-input v-model="form.crediential" :maxlength="100" name="name" required />
</el-form-item>
<el-form-item :label="$t('password')">
<el-input v-model="form.password" name="password" />
</el-form-item>
<el-button type="primary">{{ $t('login') }}</el-button>
</el-form>
<div class="form-note">
<div class="forget-password">
<router-link :to="{name: 'forgetPassword'}">{{ $t('forget_password') }}</router-link>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
const STATE = {
USER_NOT_ACTIVE: 1,
};
export default {
name: 'login',
data() {
return {
form: {
username: '',
password: '',
},
};
},
computed: {
isCredentialError() {
return false;
},
isInactiveError() {
return false;
},
isResetPasswordSuccess() {
return false;
},
},
methods: {
...mapActions(['login']),
/**
* Handle the submitting the login form.
*/
onSubmit() {
const form = {};
this.login({ form }).then(() => {
this.$route.push({ name: 'dashboard.home' });
}).catch((error) => {
const { response } = error;
const { data } = response;
if (data.error.type === 'USER_NOT_ACTIVE') {
this.current_state = STATE.USER_NOT_ACTIVE;
}
});
},
},
};
</script>

View File

@@ -1,79 +0,0 @@
<template>
<div class="reset-password" id="reset-password">
<el-form ref="form" class="form-container">
<el-form-item :label="$t('password')" prop="title">
<el-input v-model="form.password" :maxlength="100" name="password" required />
</el-form-item>
<el-form-item :label="$t('confirm_password')">
<el-input v-model="form.confirm_password" name="confirm_password" />
</el-form-item>
<el-button @click.prevent="onSubmit" type="primary">
{{ $t('reset_the_password') }}
</el-button>
<el-link>{{ $t('forget_your_password') }}</el-link>
</el-form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
const STATE = {
SUCCESS: 1,
TOKEN_INVALID: 2,
};
export default {
name: 'reset-password',
data() {
return {
form: {
password: '',
confirm_password: '',
},
};
},
props: {
token: {
required: true,
type: String,
},
},
computed: {
isSuccess() {
return this.current_state === STATE.SUCCESS;
},
isTokenInvalid() {
return this.current_state === STATE.TOKEN_INVALID;
},
},
methods: {
...mapActions(['newPassword']),
/**
* Handle the submitting the reset password form.
*/
onSubmit() {
const form = {
...this.form,
token: this.token,
};
this.newPassword({ form }).then(() => {
this.$router.push({ name: 'login' });
}).catch((error) => {
const { response } = error;
const { data } = response;
if (data.error.type === 'token.invalid') {
this.current_status = STATE.TOKEN_INVALID;
}
});
},
},
};
</script>

View File

@@ -0,0 +1,25 @@
import * as React from "react";
import {
Button,
InputGroup,
} from "@blueprintjs/core";
export default function Login() {
return (
<div class="login-page">
<InputGroup
leftIcon="filter"
placeholder="Email or phone number"
/>
<InputGroup
leftIcon="filter"
placeholder="Password"
/>
<Button>
{"Login"}
</Button>
</div>
)
}

View File

@@ -0,0 +1,20 @@
import * as React from "react";
import {
Button,
InputGroup,
} from "@blueprintjs/core";
export default function Login() {
return (
<div class="login-page">
<InputGroup
leftIcon="filter"
placeholder="Email or phone number"
/>
<Button>
{"Reset password"}
</Button>
</div>
)
}

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import authenticationRoutes from 'routes/authentication';
export default function() {
return (
<div class="authentication-page">
<Router>
<Link to="/login">{ "Login" }</Link>
<Link to="/reset_password">{ "Reset Password" }</Link>
<Switch>
{ authenticationRoutes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</Router>
</div>
);
}

View File

@@ -1,14 +0,0 @@
<template>
<div class="">
</div>
</template>
<script>
export default {
name: 'account-form',
data() {
},
};
</script>

View File

@@ -1,11 +0,0 @@
<template>
<div>
</div>
</template>
<script>
export default {
name: 'accounts-list',
};
</script>

View File

@@ -1,31 +0,0 @@
<template>
</template>
<script>
const STATE = {
LOADING: 1,
EMPTY_LIST: 2,
NO_SEARCH_RESULTS: 3,
};
export default {
name: 'customers-list',
data() {
return {
current_state: 0,
};
},
computed: {
loading() {
return this.current_state === STATE.LOADING;
},
emptyList() {
return this.current_state === STATE.EMPTY_LIST;
},
noSearchResults() {
return this.current_state === STATE.NO_SEARCH_RESULTS;
},
},
}
</script>

View File

@@ -1,37 +0,0 @@
<template>
<div id="dashboard" class="dashboard">
<DashboardSidebar />
<div class="dashboard__content">
<DashboardTopbar></DashboardTopbar>
<div class="dashboard__content-inner">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import DashboardSidebar from '@/views/Sidebar';
import DashboardTopbar from '@/views/Topbar';
export default {
name: 'dashboard',
components: {
DashboardTopbar,
DashboardSidebar,
},
};
</script>
<style lang="scss">
.dashboard{
display: flex;
flex-direction: row;
&__content{
width: 100%;
}
}
</style>

View File

@@ -1,9 +0,0 @@
<template>
<div>Home</div>
</template>
<script>
export default {
name: 'home',
};
</script>

View File

@@ -1,34 +0,0 @@
<template>
<div class="item-form" id="item-form">
<el-form ref="form" class="form-container">
<el-form-item :label="$t('item_name')" prop="title">
<el-input v-model="form.name" :maxlength="100" name="name" required />
</el-form-item>
<el-form-item :label="$t('SKU')" prop="title">
<el-input v-model="form.SKU" :maxlength="100" name="SKU" required />
</el-form-item>
<el-button type="primary">{{ $t('login') }}</el-button>
</el-form>
</div>
</template>
<script>
export default {
name: 'item-form',
data() {
return {
form: {
name: '',
},
};
},
methods: {
onSubmit() {
},
},
};
</script>

View File

@@ -1,55 +0,0 @@
<template>
<el-table v-loading="isLoading" :data="items.list" border fit highlight-current-row>
</el-table>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import Reloadable from '@/mixins/Reloadable';
const STATE = {
LOADING: 1,
};
export default {
name: 'items-datatable',
mixins: [Reloadable],
data() {
return {
current_state: 0,
page: 1,
};
},
computed: {
...mapGetters({
items: 'getItems',
}),
isLoading() {
return this.current_state === STATE.LOADING;
},
},
created() {
this.current_state = STATE.LOADING;
this.fetchData();
this.current_state = 0;
},
methods: {
...mapActions(['fetchItems']),
/**
* Handle the reload data.
*/
reloadData() {
this.fetchData();
},
/**
* Handle the fetch data of datatable.
*/
async fetchData() {
await this.fetchItems({ page: this.page });
},
},
};
</script>

View File

@@ -1,27 +0,0 @@
<template>
<el-card>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="User" name="first" :lazy="true">User</el-tab-pane>
<el-tab-pane label="Config" name="second" :lazy="true">Config</el-tab-pane>
<el-tab-pane label="Role" name="third" :lazy="true">Role</el-tab-pane>
<el-tab-pane label="Task" name="fourth" :lazy="true">Task</el-tab-pane>
</el-tabs>
</el-card>
</template>
<script>
export default {
name: 'products-list',
data() {
return {
activeName: 'first',
};
},
methods: {
handleClick(tab, event) {
},
},
};
</script>

View File

@@ -1,18 +0,0 @@
<template>
<div class="">
</div>
</template>
<script>
export default {
name: 'item-category-form',
data() {
return {
form: {
},
};
},
}
</script>

View File

@@ -1,42 +0,0 @@
<template>
<div>
<el-table v-loading="isLoading" :data="items.list" border fit highlight-current-row>
</el-table>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'items-categories',
data() {
return {
current_state: 0,
};
},
computed: {
...mapGetters({
categories: 'getItemsCategories',
}),
},
methods: {
...mapActions(['fetchItemsCategories']),
/**
* Handle the reload data of the current view.
*/
async reloadData() {
await this.fetchData();
},
/**
* Handle the fetch data of the datatable.
*/
async fetchData() {
await this.fetchItemsCategories();
},
},
};
</script>

View File

@@ -1,14 +0,0 @@
<template>
<div>Users List</div>
</template>
<script>
export default {
name: 'users-list',
afterRouteEnter(to, from, next) {
debugger;
this.$store.commit('setPageTitle', this.$t('users_list'));
next();
},
}
</script>

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Sidebar from '../../components/Sidebar';
import DashboardContent from '../../components/DashboardContent';
export default function() {
return (
<div class="dashboard" id="dashboard">
<Router>
<Sidebar />
<DashboardContent />
</Router>
</div>
)
}

View File

@@ -1,24 +0,0 @@
import Vue from 'vue';
export default {
get(resource, params) {
return Vue.axios.get(`api/${resource}`, params);
},
post(resource, params) {
return Vue.axios.post(`api/${resource}`, params);
},
update(resource, slug, params) {
return Vue.axios.put(`api/${resource}/${slug}`, params);
},
put(resource, params) {
return Vue.axios.put(`api/${resource}`, params);
},
delete(resource) {
return Vue.axios.delete(`api/${resource}`);
}
};

View File

@@ -1,59 +0,0 @@
import axios from 'axios';
import store from '~/store';
import swal from 'sweetalert2'
import Vue from 'vue';
import VueAxios from 'vue-axios';
import router from '~/routes';
// Set config defaults when creating the instance
const http = axios.create();
// request interceptor.
http.interceptors.request.use((request) => {
const token = store.getters.authToken;
const locale = Vue.i18n.locale();
if (token) {
request.headers.common['x-access-token'] = token;
}
if (locale) {
request.headers.common['Accept-Language'] = locale;
}
return request;
});
// response interceptor
http.interceptors.response.use(response => response, (error) => {
const { status } = error.response;
if (status >= 500) {
swal({
type: 'error',
title: Vue.i18n.translate('error_alert_title'),
text: Vue.i18n.translate('error_alert_text'),
reverseButtons: true,
confirmButtonText: Vue.i18n.translate('ok'),
cancelButtonText: Vue.i18n.translate('cancel')
});
}
if (status === 401) {
swal({
type: 'warning',
title: Vue.i18n.translate('token_expired_alert_title'),
text: Vue.i18n.translate('token_expired_alert_text'),
reverseButtons: true,
confirmButtonText: Vue.i18n.translate('ok'),
cancelButtonText: Vue.i18n.translate('cancel')
}).then(() => {
store.commit('removeToken');
router.push({ name: 'login' });
});
}
return Promise.reject(error)
});
Vue.use(VueAxios, http);
export default http;

View File

@@ -1,29 +0,0 @@
import Vue from 'vue';
const defaultLocale = 'en';
const loadedLangauges = [];
const currentLocale = Vue.i18n.locale();
function setLanguage(locale, messages) {
const localeMessages = messages.default;
Vue.i18n.add(locale, { ...localeMessages });
loadedLangauges.push(locale);
Vue.i18n.set(locale);
}
async function loadAsyncLanguage(locale) {
if (locale !== currentLocale) {
if (!loadedLangauges.includes(locale)) {
const messages = await import(/* webpackChunkName: "lang-[request]" */ `@/lang/${locale}/app`);
setLanguage(locale, messages);
}
}
}
(async () => {
await loadAsyncLanguage(defaultLocale);
})();
export default loadAsyncLanguage;

View File

@@ -1,9 +0,0 @@
import Vue from 'vue';
import SvgIcon from '@/components/SvgIcon';
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('../../static/icons', false, /\.svg$/);
const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req);

View File

@@ -0,0 +1,10 @@
import { combineReducers } from 'redux';
import accounts from './accounts';
import authentication from './authentication';
import users from './users';
export default combineReducers({
authentication,
users,
accounts,
});

View File

@@ -1,43 +0,0 @@
import Vue from 'vue';
import Router from 'vue-router';
import store from '@/store';
import routes from './routes';
Vue.use(Router);
const router = new Router({
mode: 'hash',
routes: [
...routes,
],
});
router.beforeEach((to, from, next) => {
const isAuthenticated = store.getters.authCheck;
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated) {
next({
name: 'login',
params: { nextUrl: to.fullPath },
});
} else {
next();
}
} else if (to.matched.some(record => record.meta.guest)) {
if (isAuthenticated) {
next({
name: 'dashboard.name',
params: {
nextUrl: to.fullPath,
},
});
} else {
next();
}
} else {
next();
}
});
export default router;

View File

@@ -1,89 +0,0 @@
const routes = [
{
name: 'auth',
path: '/',
component: () => import(/* webpackChunkName: "auth_container" */
'@/pages/Auth/AuthContainer.vue'),
children: [
{
name: 'login',
path: '/login',
component: () => import(/* webpackChunkName: "login" */
'@/pages/Auth/Login.vue'),
},
{
name: 'forgetPassword',
path: '/forget_password',
component: () => import(/* webpackChunkName: "forget_password" */
'@/pages/Auth/ForgetPassword.vue'),
},
],
},
{
name: 'dashboard',
path: '/dashboard',
component: () => import(/* webpackChunkName: "dashboard" */
'@/pages/Dashboard/Dashboard.vue'),
children: [
{
name: 'dashboard.home',
path: '/home',
component: () => import(/* webpackChunkName: "dashboard_home" */
'@/pages/Dashboard/Home.vue'),
},
/**
* Items. (Products/Services).
* --------------------------------
*/
{
name: 'dashboard.items.list',
path: '/items',
component: () => import(/* webpackChunkName: "items_list" */
'@/pages/Dashboard/Items/ItemsList.vue'),
accessControl: { resource: 'items', permissions: ['view'] },
},
{
name: 'dashboard.items.new',
path: '/items/new',
component: () => import(/* webpackChunkName: "items_form" */
'@/pages/Dashboard/Items/ItemForm.vue'),
accessControl: { resource: 'items', permissions: ['create'] },
},
/**
* Accounts
* ---------------------------
*/
{
name: 'dashboard.accounts.list',
path: '/accounts/list',
component: () => import(/* webpackChunkName: "accounts_list" */
'@/pages/Dashboard/Accounts/AccountsList.vue'),
accessControl: { resource: 'accounts', permissions: ['view'] },
},
/**
* Users.
* --------------------------------
*/
{
name: 'dashboard.users.list',
path: '/users',
component: () => import(/* webpackChunkName: "users_list" */
'@/pages/Dashboard/Users/UsersList.vue'),
accessControl: { resource: 'users', permissions: ['view'] },
},
{
name: 'dasboard.user.new',
path: '/users',
component: () => import(/* webpackChunkName: "user_form" */
'@/pages/Dashboard/Users/UserForm.vue'),
accessControl: { resource: 'users', permissions: ['create'] },
},
],
},
];
export default routes;

View File

@@ -0,0 +1,15 @@
import Login from '../pages/Authentication/Login';
import ResetPassword from '../pages/Authentication/ResetPassword';
export default [
{
path: '/login',
exact: true,
component: Login,
},
{
path: '/reset_password',
exact: true,
component: ResetPassword,
}
];

View File

@@ -0,0 +1,11 @@
import AccountsChart from '../pages/Dashboard/AccountsChart';
export default [
{
path: '/accounts/list',
component: AccountsChart,
icon: 'cut',
text: 'Chart of Accounts',
label: 'Chart of Accounts'
},
];

141
client/src/serviceWorker.js Normal file
View File

@@ -0,0 +1,141 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then(registration => {
registration.unregister();
})
.catch(error => {
console.error(error.message);
});
}
}

5
client/src/setupTests.js Normal file
View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

View File

@@ -1,25 +0,0 @@
import Vue from 'vue';
import Vuex from 'vuex';
import vuexI18n from 'vuex-i18n';
import sidebar from '@/store/modules/sidebar';
import app from '@/store/modules/app';
import items from '@/store/modules/items';
import customers from '@/store/modules/customers';
const debug = process.env.NODE_ENV !== 'production';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
app,
sidebar,
items,
customers,
},
strict: debug,
});
Vue.use(vuexI18n.plugin, store);
export default store;

View File

@@ -1,41 +0,0 @@
/* eslint-disable no-param-reassign */
const state = {
appName: 'Ratteb',
appVersion: '1.0.0',
appBuild: '1000',
pageTitle: 'Welcome',
actions: [],
contentState: 0,
sidebarOpened: true,
};
const getters = {
getAppName: s => s.appName,
getAppVersion: s => s.appVersion,
getAppBuild: s => s.appBuild,
getPageTitle: s => s.pageTitle,
};
const actions = {
setPageTitle(s, title) {
s.title = title;
},
};
const mutations = {
toggleSidebar() {
state.sidebarOpened = !state.sidebarOpened;
if (state.sidebarOpened) {
localStorage.set('sidebarStatus', 1)
} else {
localStorage.set('sidebarStatus', 0)
}
},
}
export default { state, actions, mutations, getters };

View File

@@ -1,55 +0,0 @@
import ApiService from '@/plugins/api-service';
const state = {
token: localStorage.getItem('token') || '',
errors: {},
role: {},
};
const getters = {
authCheck: s => s.token,
authToken: s => s.token,
authorizedUserRole: s => s.role,
};
const actions = {
/**
* User login login authentication request.
*/
async login({ commit }, { form }) {
const response = await ApiService.post('auth/login', form);
const { data } = response;
if (data.token) {
commit('setToken', data.token);
}
return data;
},
/**
* Send reset password via email or SMS.
*/
sendResetPassword({}, { email }) {
return ApiService.post('auth/send_reset_password', { email });
},
newPassword({}, { form }) {
return ApiService.post('auth/new_password', form);
},
};
const mutations = {
setToken(s, token) {
localStorage.setItem('token', token);
s.token = token;
},
removeToken(s) {
localStorage.removeItem('token');
s.token = '';
},
};
export default { state, actions, mutations, getters };

View File

@@ -1,75 +0,0 @@
import ApiService from '@/plugins/api-service';
const state = {
list: {},
details: [],
};
const getters = {
getCustomers: s => s.list,
getCustomer: s => id => s.details.find(i => i.id === id),
};
const actions = {
/**
* Fetches customers with current page.
*/
async fetchCustomers({ commit }, { query } = {}) {
const response = await ApiService.post('customers', { params: query });
const { data } = response;
const { count } = data.pagination;
commit('setItems', data);
if (count) {
commit('setSidebarItemCount', { name: 'customers', count });
}
return data;
},
/**
* Fetch the given customer details.
*/
async fetchCustomer({ commit }, { id }) {
const response = await ApiService.get(`customers/${id}`);
const { data } = response;
commit('setItem', data);
return data;
},
/**
* Delete the given customer.
*/
async deleteCustomer({}, { id }) {
return ApiService.delete(`customers/${id}`);
},
/**
* Submit the new customer.
*/
async newCustomer({}, { form }) {
return ApiService.post('customers', form);
},
/**
* Update details the given customer.
*/
async updateCustomer({}, { form, id }) {
return ApiService.post(`customers/${id}`, form);
},
};
const mutations = {
setCustomers(s, items) {
s.list = items;
},
setCustomer(s, item) {
s.details = s.details.filter(i => i.id !== item.id);
s.details.push(item);
},
};
export default { state, actions, mutations, getters };

View File

@@ -1,28 +0,0 @@
const state = {
logs: [],
};
const mutations = {
ADD_ERROR_LOG: (s, log) => {
s.logs.push(log);
},
CLEAR_ERROR_LOG: (s) => {
s.logs.splice(0);
}
}
const actions = {
addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log);
},
clearErrorLog({ commit }) {
commit('CLEAR_ERROR_LOG');
},
}
export default {
namespaced: true,
state,
mutations,
actions,
};

View File

@@ -1,119 +0,0 @@
import ApiService from '@/plugins/api-service';
const state = {
list: {},
details: [],
categories: {},
categoriesDetails: [],
};
const getters = {
getItems: s => s.list,
getItem: s => id => s.details.find(i => i.id === id),
getItemsCategories: s => s.categories,
getItemCategory: s => id => s.categoriesDetails.find(i => i.id === id),
};
const actions = {
/**
* Fetches the products/services list.
*/
async fetchItems({ commit }, { query } = {}) {
const response = await ApiService.post('items', { params: query });
const { data } = response;
const { count } = data.pagination;
commit('setItems', data);
if (count) {
commit('setSidebarItemCount', {
name: 'items', count,
});
}
return data;
},
/**
* Fetch the given product/service details.
*/
async fetchItem({ commit }, { id }) {
const response = await ApiService.get(`${id}`);
const { data } = response;
commit('setItem', data);
return data;
},
/**
* Delete the given product/service.
*/
async deleteItem({ commit }, { id }) {
return ApiService.delete(`items/${id}`);
},
async newItem({}, { form }) {
return ApiService.post('items', form);
},
async updateItem({}, { form, id }) {
return ApiService.post(`items/${id}`, form);
},
/**
* Fetches items categories paged list.
*/
async fetchItemsCategories({}, { query }) {
return ApiService.get('/items_categories', { params: query });
},
/**
* Fetch details of the given item category.
*/
async fetchItemCategory({}, { id }) {
return ApiService.get(`/item/${id}`);
},
/**
* Delete the given item category.
*/
async deleteItemCategory({}, { id }) {
return ApiService.delete(`/items_categories/${id}`);
},
/**
* Post a new item category.
*/
async newItemCategory({}, { form }) {
return ApiService.post('/items_categories', form);
},
/**
* Update details of the given item category.
*/
async updateItemCategory({}, { id, form }) {
return ApiService.post(`/items_categories/${id}`, form);
},
};
const mutations = {
setItems(s, items) {
s.list = items;
},
setItem(s, item) {
s.details = s.details.filter(i => i.id !== item.id);
s.details.push(item);
},
setItemCategories(s, categories) {
s.categories = categories;
},
setItemCategory(s, category) {
s.categoriesDetails = s.categoriesDetails.filter(i => i.id !== category.id);
s.categoriesDetails.push(category);
},
};
export default { state, actions, mutations, getters };

View File

@@ -1,93 +0,0 @@
const state = {
items: [
{
name: 'Home',
to: 'dashboard.home',
icon: 'home',
count: null,
},
{
name: 'Products',
to: 'dashboard.items.list',
},
{
name: 'Customers',
to: 'dashboard.home',
},
{
name: 'Suppliers',
to: 'dashboard.home',
},
{
name: 'Reports',
to: 'dashboard.home',
},
{
name: 'Users',
to: 'dashboard.users.list',
children: {
name: 'New User',
to: 'dashboard.user.new',
},
},
{
name: 'Accounting',
to: 'dashboard.accounts.list',
},
],
quickActions: [
{
route: 'dashboard.items.list',
actions: [
{
dialog: 'global-search',
label: 'Search',
icon: 'search',
},
],
},
{
route: 'dashboard.items.list',
actions: [
{
dialog: 'test',
},
],
},
],
};
const getters = {
getSidebarItems: s => s.items,
getSidebarItem: s => name => s.items.find(item => item.name === name),
getAllQuickActions: s => s.quickActions,
getQuickActions: s => (route) => {
const foundDefault = s.quickActions.find(q => q.default === true);
const found = s.quickActions.find(q => q.route === route);
const defaultActions = foundDefault ? foundDefault.actions : [];
return found ? [...defaultActions, found.actions] : defaultActions;
},
};
const actions = {
/**
* Set count to the given sidebar item.
*/
setSidebarItemCount(s, { name, count }) {
s.items = s.items.map((item) => {
const mapped = { ...item };
if (item.name === name) {
mapped.count = count;
}
return mapped;
});
},
};
export default { state, getters, actions };

38
client/src/style/App.css Normal file
View File

@@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -1,994 +0,0 @@
/* Element Chalk Variables */
// Special comment for theme configurator
// type|skipAutoTranslation|Category|Order
// skipAutoTranslation 1
/* Transition
-------------------------- */
$--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default;
$--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--fade-linear-transition: opacity 200ms linear !default;
$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default;
$--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409EFF !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */
$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */
$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */
$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
/// color|1|Functional Color|1
$--color-success: #67C23A !default;
/// color|1|Functional Color|1
$--color-warning: #E6A23C !default;
/// color|1|Functional Color|1
$--color-danger: #F56C6C !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;
$--color-success-light: mix($--color-white, $--color-success, 80%) !default;
$--color-warning-light: mix($--color-white, $--color-warning, 80%) !default;
$--color-danger-light: mix($--color-white, $--color-danger, 80%) !default;
$--color-info-light: mix($--color-white, $--color-info, 80%) !default;
$--color-success-lighter: mix($--color-white, $--color-success, 90%) !default;
$--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default;
$--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default;
$--color-info-lighter: mix($--color-white, $--color-info, 90%) !default;
/// color|1|Font Color|2
$--color-text-primary: #303133 !default;
/// color|1|Font Color|2
$--color-text-regular: #606266 !default;
/// color|1|Font Color|2
$--color-text-secondary: #909399 !default;
/// color|1|Font Color|2
$--color-text-placeholder: #C0C4CC !default;
/// color|1|Border Color|3
$--border-color-base: #DCDFE6 !default;
/// color|1|Border Color|3
$--border-color-light: #E4E7ED !default;
/// color|1|Border Color|3
$--border-color-lighter: #EBEEF5 !default;
/// color|1|Border Color|3
$--border-color-extra-light: #F2F6FC !default;
// Background
/// color|1|Background Color|4
$--background-color-base: #F5F7FA !default;
/* Link
-------------------------- */
$--link-color: $--color-primary-light-2 !default;
$--link-hover-color: $--color-primary !default;
/* Border
-------------------------- */
$--border-width-base: 1px !default;
$--border-style-base: solid !default;
$--border-color-hover: $--color-text-placeholder !default;
$--border-base: $--border-width-base $--border-style-base $--border-color-base !default;
/// borderRadius|1|Radius|0
$--border-radius-base: 4px !default;
/// borderRadius|1|Radius|0
$--border-radius-small: 2px !default;
/// borderRadius|1|Radius|0
$--border-radius-circle: 100% !default;
/// borderRadius|1|Radius|0
$--border-radius-zero: 0 !default;
// Box-shadow
/// boxShadow|1|Shadow|1
$--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default;
// boxShadow|1|Shadow|1
$--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default;
/// boxShadow|1|Shadow|1
$--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default;
/* Fill
-------------------------- */
$--fill-base: $--color-white !default;
/* Typography
-------------------------- */
$--font-path: 'fonts' !default;
$--font-display: 'auto' !default;
/// fontSize|1|Font Size|0
$--font-size-extra-large: 20px !default;
/// fontSize|1|Font Size|0
$--font-size-large: 18px !default;
/// fontSize|1|Font Size|0
$--font-size-medium: 16px !default;
/// fontSize|1|Font Size|0
$--font-size-base: 14px !default;
/// fontSize|1|Font Size|0
$--font-size-small: 13px !default;
/// fontSize|1|Font Size|0
$--font-size-extra-small: 12px !default;
/// fontWeight|1|Font Weight|1
$--font-weight-primary: 500 !default;
/// fontWeight|1|Font Weight|1
$--font-weight-secondary: 100 !default;
/// fontLineHeight|1|Line Height|2
$--font-line-height-primary: 24px !default;
/// fontLineHeight|1|Line Height|2
$--font-line-height-secondary: 16px !default;
$--font-color-disabled-base: #bbb !default;
/* Size
-------------------------- */
$--size-base: 14px !default;
/* z-index
-------------------------- */
$--index-normal: 1 !default;
$--index-top: 1000 !default;
$--index-popper: 2000 !default;
/* Disable base
-------------------------- */
$--disabled-fill-base: $--background-color-base !default;
$--disabled-color-base: $--color-text-placeholder !default;
$--disabled-border-base: $--border-color-light !default;
/* Icon
-------------------------- */
$--icon-color: #666 !default;
$--icon-color-base: $--color-info !default;
/* Checkbox
-------------------------- */
/// fontSize||Font|1
$--checkbox-font-size: 14px !default;
/// fontWeight||Font|1
$--checkbox-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--checkbox-font-color: $--color-text-regular !default;
$--checkbox-input-height: 14px !default;
$--checkbox-input-width: 14px !default;
/// borderRadius||Border|2
$--checkbox-border-radius: $--border-radius-small !default;
/// color||Color|0
$--checkbox-background-color: $--color-white !default;
$--checkbox-input-border: $--border-base !default;
/// color||Color|0
$--checkbox-disabled-border-color: $--border-color-base !default;
$--checkbox-disabled-input-fill: #edf2fc !default;
$--checkbox-disabled-icon-color: $--color-text-placeholder !default;
$--checkbox-disabled-checked-input-fill: $--border-color-extra-light !default;
$--checkbox-disabled-checked-input-border-color: $--border-color-base !default;
$--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--checkbox-checked-font-color: $--color-primary !default;
$--checkbox-checked-input-border-color: $--color-primary !default;
/// color||Color|0
$--checkbox-checked-background-color: $--color-primary !default;
$--checkbox-checked-icon-color: $--fill-base !default;
$--checkbox-input-border-color-hover: $--color-primary !default;
/// height||Other|4
$--checkbox-bordered-height: 40px !default;
/// padding||Spacing|3
$--checkbox-bordered-padding: 9px 20px 9px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-medium-padding: 7px 20px 7px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-small-padding: 5px 15px 5px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-mini-padding: 3px 15px 3px 10px !default;
$--checkbox-bordered-medium-input-height: 14px !default;
$--checkbox-bordered-medium-input-width: 14px !default;
/// height||Other|4
$--checkbox-bordered-medium-height: 36px !default;
$--checkbox-bordered-small-input-height: 12px !default;
$--checkbox-bordered-small-input-width: 12px !default;
/// height||Other|4
$--checkbox-bordered-small-height: 32px !default;
$--checkbox-bordered-mini-input-height: 12px !default;
$--checkbox-bordered-mini-input-width: 12px !default;
/// height||Other|4
$--checkbox-bordered-mini-height: 28px !default;
/// color||Color|0
$--checkbox-button-checked-background-color: $--color-primary !default;
/// color||Color|0
$--checkbox-button-checked-font-color: $--color-white !default;
/// color||Color|0
$--checkbox-button-checked-border-color: $--color-primary !default;
/* Radio
-------------------------- */
/// fontSize||Font|1
$--radio-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--radio-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--radio-font-color: $--color-text-regular !default;
$--radio-input-height: 14px !default;
$--radio-input-width: 14px !default;
/// borderRadius||Border|2
$--radio-input-border-radius: $--border-radius-circle !default;
/// color||Color|0
$--radio-input-background-color: $--color-white !default;
$--radio-input-border: $--border-base !default;
/// color||Color|0
$--radio-input-border-color: $--border-color-base !default;
/// color||Color|0
$--radio-icon-color: $--color-white !default;
$--radio-disabled-input-border-color: $--disabled-border-base !default;
$--radio-disabled-input-fill: $--disabled-fill-base !default;
$--radio-disabled-icon-color: $--disabled-fill-base !default;
$--radio-disabled-checked-input-border-color: $--disabled-border-base !default;
$--radio-disabled-checked-input-fill: $--disabled-fill-base !default;
$--radio-disabled-checked-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--radio-checked-font-color: $--color-primary !default;
/// color||Color|0
$--radio-checked-input-border-color: $--color-primary !default;
/// color||Color|0
$--radio-checked-input-background-color: $--color-white !default;
/// color||Color|0
$--radio-checked-icon-color: $--color-primary !default;
$--radio-input-border-color-hover: $--color-primary !default;
$--radio-bordered-height: 40px !default;
$--radio-bordered-padding: 12px 20px 0 10px !default;
$--radio-bordered-medium-padding: 10px 20px 0 10px !default;
$--radio-bordered-small-padding: 8px 15px 0 10px !default;
$--radio-bordered-mini-padding: 6px 15px 0 10px !default;
$--radio-bordered-medium-input-height: 14px !default;
$--radio-bordered-medium-input-width: 14px !default;
$--radio-bordered-medium-height: 36px !default;
$--radio-bordered-small-input-height: 12px !default;
$--radio-bordered-small-input-width: 12px !default;
$--radio-bordered-small-height: 32px !default;
$--radio-bordered-mini-input-height: 12px !default;
$--radio-bordered-mini-input-width: 12px !default;
$--radio-bordered-mini-height: 28px !default;
/// fontSize||Font|1
$--radio-button-font-size: $--font-size-base !default;
/// color||Color|0
$--radio-button-checked-background-color: $--color-primary !default;
/// color||Color|0
$--radio-button-checked-font-color: $--color-white !default;
/// color||Color|0
$--radio-button-checked-border-color: $--color-primary !default;
$--radio-button-disabled-checked-fill: $--border-color-extra-light !default;
/* Select
-------------------------- */
$--select-border-color-hover: $--border-color-hover !default;
$--select-disabled-border: $--disabled-border-base !default;
/// fontSize||Font|1
$--select-font-size: $--font-size-base !default;
$--select-close-hover-color: $--color-text-secondary !default;
$--select-input-color: $--color-text-placeholder !default;
$--select-multiple-input-color: #666 !default;
/// color||Color|0
$--select-input-focus-border-color: $--color-primary !default;
/// fontSize||Font|1
$--select-input-font-size: 14px !default;
$--select-option-color: $--color-text-regular !default;
$--select-option-disabled-color: $--color-text-placeholder !default;
$--select-option-disabled-background: $--color-white !default;
/// height||Other|4
$--select-option-height: 34px !default;
$--select-option-hover-background: $--background-color-base !default;
/// color||Color|0
$--select-option-selected-font-color: $--color-primary !default;
$--select-option-selected-hover: $--background-color-base !default;
$--select-group-color: $--color-info !default;
$--select-group-height: 30px !default;
$--select-group-font-size: 12px !default;
$--select-dropdown-background: $--color-white !default;
$--select-dropdown-shadow: $--box-shadow-light !default;
$--select-dropdown-empty-color: #999 !default;
/// height||Other|4
$--select-dropdown-max-height: 274px !default;
$--select-dropdown-padding: 6px 0 !default;
$--select-dropdown-empty-padding: 10px 0 !default;
$--select-dropdown-border: solid 1px $--border-color-light !default;
/* Alert
-------------------------- */
$--alert-padding: 8px 16px !default;
/// borderRadius||Border|2
$--alert-border-radius: $--border-radius-base !default;
/// fontSize||Font|1
$--alert-title-font-size: 13px !default;
/// fontSize||Font|1
$--alert-description-font-size: 12px !default;
/// fontSize||Font|1
$--alert-close-font-size: 12px !default;
/// fontSize||Font|1
$--alert-close-customed-font-size: 13px !default;
$--alert-success-color: $--color-success-lighter !default;
$--alert-info-color: $--color-info-lighter !default;
$--alert-warning-color: $--color-warning-lighter !default;
$--alert-danger-color: $--color-danger-lighter !default;
/// height||Other|4
$--alert-icon-size: 16px !default;
/// height||Other|4
$--alert-icon-large-size: 28px !default;
/* MessageBox
-------------------------- */
/// color||Color|0
$--messagebox-title-color: $--color-text-primary !default;
$--msgbox-width: 420px !default;
$--msgbox-border-radius: 4px !default;
/// fontSize||Font|1
$--messagebox-font-size: $--font-size-large !default;
/// fontSize||Font|1
$--messagebox-content-font-size: $--font-size-base !default;
/// color||Color|0
$--messagebox-content-color: $--color-text-regular !default;
/// fontSize||Font|1
$--messagebox-error-font-size: 12px !default;
$--msgbox-padding-primary: 15px !default;
/// color||Color|0
$--messagebox-success-color: $--color-success !default;
/// color||Color|0
$--messagebox-info-color: $--color-info !default;
/// color||Color|0
$--messagebox-warning-color: $--color-warning !default;
/// color||Color|0
$--messagebox-danger-color: $--color-danger !default;
/* Message
-------------------------- */
$--message-shadow: $--box-shadow-base !default;
$--message-min-width: 380px !default;
$--message-background-color: #edf2fc !default;
$--message-padding: 15px 15px 15px 20px !default;
/// color||Color|0
$--message-close-icon-color: $--color-text-placeholder !default;
/// height||Other|4
$--message-close-size: 16px !default;
/// color||Color|0
$--message-close-hover-color: $--color-text-secondary !default;
/// color||Color|0
$--message-success-font-color: $--color-success !default;
/// color||Color|0
$--message-info-font-color: $--color-info !default;
/// color||Color|0
$--message-warning-font-color: $--color-warning !default;
/// color||Color|0
$--message-danger-font-color: $--color-danger !default;
/* Notification
-------------------------- */
$--notification-width: 330px !default;
/// padding||Spacing|3
$--notification-padding: 14px 26px 14px 13px !default;
$--notification-radius: 8px !default;
$--notification-shadow: $--box-shadow-light !default;
/// color||Color|0
$--notification-border-color: $--border-color-lighter !default;
$--notification-icon-size: 24px !default;
$--notification-close-font-size: $--message-close-size !default;
$--notification-group-margin-left: 13px !default;
$--notification-group-margin-right: 8px !default;
/// fontSize||Font|1
$--notification-content-font-size: $--font-size-base !default;
/// color||Color|0
$--notification-content-color: $--color-text-regular !default;
/// fontSize||Font|1
$--notification-title-font-size: 16px !default;
/// color||Color|0
$--notification-title-color: $--color-text-primary !default;
/// color||Color|0
$--notification-close-color: $--color-text-secondary !default;
/// color||Color|0
$--notification-close-hover-color: $--color-text-regular !default;
/// color||Color|0
$--notification-success-icon-color: $--color-success !default;
/// color||Color|0
$--notification-info-icon-color: $--color-info !default;
/// color||Color|0
$--notification-warning-icon-color: $--color-warning !default;
/// color||Color|0
$--notification-danger-icon-color: $--color-danger !default;
/* Input
-------------------------- */
$--input-font-size: $--font-size-base !default;
/// color||Color|0
$--input-font-color: $--color-text-regular !default;
/// height||Other|4
$--input-width: 140px !default;
/// height||Other|4
$--input-height: 40px !default;
$--input-border: $--border-base !default;
$--input-border-color: $--border-color-base !default;
/// borderRadius||Border|2
$--input-border-radius: $--border-radius-base !default;
$--input-border-color-hover: $--border-color-hover !default;
/// color||Color|0
$--input-background-color: $--color-white !default;
$--input-fill-disabled: $--disabled-fill-base !default;
$--input-color-disabled: $--font-color-disabled-base !default;
/// color||Color|0
$--input-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--input-placeholder-color: $--color-text-placeholder !default;
$--input-max-width: 314px !default;
$--input-hover-border: $--border-color-hover !default;
$--input-clear-hover-color: $--color-text-secondary !default;
$--input-focus-border: $--color-primary !default;
$--input-focus-fill: $--color-white !default;
$--input-disabled-fill: $--disabled-fill-base !default;
$--input-disabled-border: $--disabled-border-base !default;
$--input-disabled-color: $--disabled-color-base !default;
$--input-disabled-placeholder-color: $--color-text-placeholder !default;
/// fontSize||Font|1
$--input-medium-font-size: 14px !default;
/// height||Other|4
$--input-medium-height: 36px !default;
/// fontSize||Font|1
$--input-small-font-size: 13px !default;
/// height||Other|4
$--input-small-height: 32px !default;
/// fontSize||Font|1
$--input-mini-font-size: 12px !default;
/// height||Other|4
$--input-mini-height: 28px !default;
/* Cascader
-------------------------- */
/// color||Color|0
$--cascader-menu-font-color: $--color-text-regular !default;
/// color||Color|0
$--cascader-menu-selected-font-color: $--color-primary !default;
$--cascader-menu-fill: $--fill-base !default;
$--cascader-menu-font-size: $--font-size-base !default;
$--cascader-menu-radius: $--border-radius-base !default;
$--cascader-menu-border: solid 1px $--border-color-light !default;
$--cascader-menu-shadow: $--box-shadow-light !default;
$--cascader-node-background-hover: $--background-color-base !default;
$--cascader-node-color-disabled:$--color-text-placeholder !default;
$--cascader-color-empty:$--color-text-placeholder !default;
$--cascader-tag-background: #f0f2f5;
/* Group
-------------------------- */
$--group-option-flex: 0 0 (1/5) * 100% !default;
$--group-option-offset-bottom: 12px !default;
$--group-option-fill-hover: rgba($--color-black, 0.06) !default;
$--group-title-color: $--color-black !default;
$--group-title-font-size: $--font-size-base !default;
$--group-title-width: 66px !default;
/* Tab
-------------------------- */
$--tab-font-size: $--font-size-base !default;
$--tab-border-line: 1px solid #e4e4e4 !default;
$--tab-header-color-active: $--color-text-secondary !default;
$--tab-header-color-hover: $--color-text-regular !default;
$--tab-header-color: $--color-text-regular !default;
$--tab-header-fill-active: rgba($--color-black, 0.06) !default;
$--tab-header-fill-hover: rgba($--color-black, 0.06) !default;
$--tab-vertical-header-width: 90px !default;
$--tab-vertical-header-count-color: $--color-white !default;
$--tab-vertical-header-count-fill: $--color-text-secondary !default;
/* Button
-------------------------- */
/// fontSize||Font|1
$--button-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--button-font-weight: $--font-weight-primary !default;
/// borderRadius||Border|2
$--button-border-radius: $--border-radius-base !default;
/// padding||Spacing|3
$--button-padding-vertical: 12px !default;
/// padding||Spacing|3
$--button-padding-horizontal: 20px !default;
/// fontSize||Font|1
$--button-medium-font-size: $--font-size-base !default;
/// borderRadius||Border|2
$--button-medium-border-radius: $--border-radius-base !default;
/// padding||Spacing|3
$--button-medium-padding-vertical: 10px !default;
/// padding||Spacing|3
$--button-medium-padding-horizontal: 20px !default;
/// fontSize||Font|1
$--button-small-font-size: 12px !default;
$--button-small-border-radius: #{$--border-radius-base - 1} !default;
/// padding||Spacing|3
$--button-small-padding-vertical: 9px !default;
/// padding||Spacing|3
$--button-small-padding-horizontal: 15px !default;
/// fontSize||Font|1
$--button-mini-font-size: 12px !default;
$--button-mini-border-radius: #{$--border-radius-base - 1} !default;
/// padding||Spacing|3
$--button-mini-padding-vertical: 7px !default;
/// padding||Spacing|3
$--button-mini-padding-horizontal: 15px !default;
/// color||Color|0
$--button-default-font-color: $--color-text-regular !default;
/// color||Color|0
$--button-default-background-color: $--color-white !default;
/// color||Color|0
$--button-default-border-color: $--border-color-base !default;
/// color||Color|0
$--button-disabled-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--button-disabled-background-color: $--color-white !default;
/// color||Color|0
$--button-disabled-border-color: $--border-color-lighter !default;
/// color||Color|0
$--button-primary-border-color: $--color-primary !default;
/// color||Color|0
$--button-primary-font-color: $--color-white !default;
/// color||Color|0
$--button-primary-background-color: $--color-primary !default;
/// color||Color|0
$--button-success-border-color: $--color-success !default;
/// color||Color|0
$--button-success-font-color: $--color-white !default;
/// color||Color|0
$--button-success-background-color: $--color-success !default;
/// color||Color|0
$--button-warning-border-color: $--color-warning !default;
/// color||Color|0
$--button-warning-font-color: $--color-white !default;
/// color||Color|0
$--button-warning-background-color: $--color-warning !default;
/// color||Color|0
$--button-danger-border-color: $--color-danger !default;
/// color||Color|0
$--button-danger-font-color: $--color-white !default;
/// color||Color|0
$--button-danger-background-color: $--color-danger !default;
/// color||Color|0
$--button-info-border-color: $--color-info !default;
/// color||Color|0
$--button-info-font-color: $--color-white !default;
/// color||Color|0
$--button-info-background-color: $--color-info !default;
$--button-hover-tint-percent: 20% !default;
$--button-active-shade-percent: 10% !default;
/* cascader
-------------------------- */
$--cascader-height: 200px !default;
/* Switch
-------------------------- */
/// color||Color|0
$--switch-on-color: $--color-primary !default;
/// color||Color|0
$--switch-off-color: $--border-color-base !default;
/// fontSize||Font|1
$--switch-font-size: $--font-size-base !default;
$--switch-core-border-radius: 10px !default;
// height||Other|4 TODO: width 代码写死的40px 所以下面这三个属性都没意义
$--switch-width: 40px !default;
// height||Other|4
$--switch-height: 20px !default;
// height||Other|4
$--switch-button-size: 16px !default;
/* Dialog
-------------------------- */
$--dialog-background-color: $--color-white !default;
$--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default;
/// fontSize||Font|1
$--dialog-title-font-size: $--font-size-large !default;
/// fontSize||Font|1
$--dialog-content-font-size: 14px !default;
/// fontLineHeight||LineHeight|2
$--dialog-font-line-height: $--font-line-height-primary !default;
/// padding||Spacing|3
$--dialog-padding-primary: 20px !default;
/* Table
-------------------------- */
/// color||Color|0
$--table-border-color: $--border-color-lighter !default;
$--table-border: 1px solid $--table-border-color !default;
/// color||Color|0
$--table-font-color: $--color-text-regular !default;
/// color||Color|0
$--table-header-font-color: $--color-text-secondary !default;
/// color||Color|0
$--table-row-hover-background-color: $--background-color-base !default;
$--table-current-row-background-color: $--color-primary-light-9 !default;
/// color||Color|0
$--table-header-background-color: $--color-white !default;
$--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default;
/* Pagination
-------------------------- */
/// fontSize||Font|1
$--pagination-font-size: 13px !default;
/// color||Color|0
$--pagination-background-color: $--color-white !default;
/// color||Color|0
$--pagination-font-color: $--color-text-primary !default;
$--pagination-border-radius: 3px !default;
/// color||Color|0
$--pagination-button-color: $--color-text-primary !default;
/// height||Other|4
$--pagination-button-width: 35.5px !default;
/// height||Other|4
$--pagination-button-height: 28px !default;
/// color||Color|0
$--pagination-button-disabled-color: $--color-text-placeholder !default;
/// color||Color|0
$--pagination-button-disabled-background-color: $--color-white !default;
/// color||Color|0
$--pagination-hover-color: $--color-primary !default;
/* Popup
-------------------------- */
/// color||Color|0
$--popup-modal-background-color: $--color-black !default;
/// opacity||Other|1
$--popup-modal-opacity: 0.5 !default;
/* Popover
-------------------------- */
/// color||Color|0
$--popover-background-color: $--color-white !default;
/// fontSize||Font|1
$--popover-font-size: $--font-size-base !default;
/// color||Color|0
$--popover-border-color: $--border-color-lighter !default;
$--popover-arrow-size: 6px !default;
/// padding||Spacing|3
$--popover-padding: 12px !default;
$--popover-padding-large: 18px 20px !default;
/// fontSize||Font|1
$--popover-title-font-size: 16px !default;
/// color||Color|0
$--popover-title-font-color: $--color-text-primary !default;
/* Tooltip
-------------------------- */
/// color|1|Color|0
$--tooltip-fill: $--color-text-primary !default;
/// color|1|Color|0
$--tooltip-color: $--color-white !default;
/// fontSize||Font|1
$--tooltip-font-size: 12px !default;
/// color||Color|0
$--tooltip-border-color: $--color-text-primary !default;
$--tooltip-arrow-size: 6px !default;
/// padding||Spacing|3
$--tooltip-padding: 10px !default;
/* Tag
-------------------------- */
/// color||Color|0
$--tag-info-color: $--color-info !default;
/// color||Color|0
$--tag-primary-color: $--color-primary !default;
/// color||Color|0
$--tag-success-color: $--color-success !default;
/// color||Color|0
$--tag-warning-color: $--color-warning !default;
/// color||Color|0
$--tag-danger-color: $--color-danger !default;
/// fontSize||Font|1
$--tag-font-size: 12px !default;
$--tag-border-radius: 4px !default;
$--tag-padding: 0 10px !default;
/* Tree
-------------------------- */
/// color||Color|0
$--tree-node-hover-background-color: $--background-color-base !default;
/// color||Color|0
$--tree-font-color: $--color-text-regular !default;
/// color||Color|0
$--tree-expand-icon-color: $--color-text-placeholder !default;
/* Dropdown
-------------------------- */
$--dropdown-menu-box-shadow: $--box-shadow-light !default;
$--dropdown-menuItem-hover-fill: $--color-primary-light-9 !default;
$--dropdown-menuItem-hover-color: $--link-color !default;
/* Badge
-------------------------- */
/// color||Color|0
$--badge-background-color: $--color-danger !default;
$--badge-radius: 10px !default;
/// fontSize||Font|1
$--badge-font-size: 12px !default;
/// padding||Spacing|3
$--badge-padding: 6px !default;
/// height||Other|4
$--badge-size: 18px !default;
/* Card
--------------------------*/
/// color||Color|0
$--card-border-color: $--border-color-lighter !default;
$--card-border-radius: 4px !default;
/// padding||Spacing|3
$--card-padding: 20px !default;
/* Slider
--------------------------*/
/// color||Color|0
$--slider-main-background-color: $--color-primary !default;
/// color||Color|0
$--slider-runway-background-color: $--border-color-light !default;
$--slider-button-hover-color: mix($--color-primary, black, 97%) !default;
$--slider-stop-background-color: $--color-white !default;
$--slider-disable-color: $--color-text-placeholder !default;
$--slider-margin: 16px 0 !default;
$--slider-border-radius: 3px !default;
/// height|1|Other|4
$--slider-height: 6px !default;
/// height||Other|4
$--slider-button-size: 16px !default;
$--slider-button-wrapper-size: 36px !default;
$--slider-button-wrapper-offset: -15px !default;
/* Steps
--------------------------*/
$--steps-border-color: $--disabled-border-base !default;
$--steps-border-radius: 4px !default;
$--steps-padding: 20px !default;
/* Menu
--------------------------*/
/// fontSize||Font|1
$--menu-item-font-size: $--font-size-base !default;
/// color||Color|0
$--menu-item-font-color: $--color-text-primary !default;
/// color||Color|0
$--menu-background-color: $--color-white !default;
$--menu-item-hover-fill: $--color-primary-light-9 !default;
/* Rate
--------------------------*/
$--rate-height: 20px !default;
/// fontSize||Font|1
$--rate-font-size: $--font-size-base !default;
/// height||Other|3
$--rate-icon-size: 18px !default;
/// margin||Spacing|2
$--rate-icon-margin: 6px !default;
$--rate-icon-color: $--color-text-placeholder !default;
/* DatePicker
--------------------------*/
$--datepicker-font-color: $--color-text-regular !default;
/// color|1|Color|0
$--datepicker-off-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--datepicker-header-font-color: $--color-text-regular !default;
$--datepicker-icon-color: $--color-text-primary !default;
$--datepicker-border-color: $--disabled-border-base !default;
$--datepicker-inner-border-color: #e4e4e4 !default;
/// color||Color|0
$--datepicker-inrange-background-color: $--border-color-extra-light !default;
/// color||Color|0
$--datepicker-inrange-hover-background-color: $--border-color-extra-light !default;
/// color||Color|0
$--datepicker-active-color: $--color-primary !default;
/// color||Color|0
$--datepicker-hover-font-color: $--color-primary !default;
$--datepicker-cell-hover-color: #fff !default;
/* Loading
--------------------------*/
/// height||Other|4
$--loading-spinner-size: 42px !default;
/// height||Other|4
$--loading-fullscreen-spinner-size: 50px !default;
/* Scrollbar
--------------------------*/
$--scrollbar-background-color: rgba($--color-text-secondary, .3) !default;
$--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default;
/* Carousel
--------------------------*/
/// fontSize||Font|1
$--carousel-arrow-font-size: 12px !default;
$--carousel-arrow-size: 36px !default;
$--carousel-arrow-background: rgba(31, 45, 61, 0.11) !default;
$--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23) !default;
/// width||Other|4
$--carousel-indicator-width: 30px !default;
/// height||Other|4
$--carousel-indicator-height: 2px !default;
$--carousel-indicator-padding-horizontal: 4px !default;
$--carousel-indicator-padding-vertical: 12px !default;
$--carousel-indicator-out-color: $--border-color-hover !default;
/* Collapse
--------------------------*/
/// color||Color|0
$--collapse-border-color: $--border-color-lighter !default;
/// height||Other|4
$--collapse-header-height: 48px !default;
/// color||Color|0
$--collapse-header-background-color: $--color-white !default;
/// color||Color|0
$--collapse-header-font-color: $--color-text-primary !default;
/// fontSize||Font|1
$--collapse-header-font-size: 13px !default;
/// color||Color|0
$--collapse-content-background-color: $--color-white !default;
/// fontSize||Font|1
$--collapse-content-font-size: 13px !default;
/// color||Color|0
$--collapse-content-font-color: $--color-text-primary !default;
/* Transfer
--------------------------*/
$--transfer-border-color: $--border-color-lighter !default;
$--transfer-border-radius: $--border-radius-base !default;
/// height||Other|4
$--transfer-panel-width: 200px !default;
/// height||Other|4
$--transfer-panel-header-height: 40px !default;
/// color||Color|0
$--transfer-panel-header-background-color: $--background-color-base !default;
/// height||Other|4
$--transfer-panel-footer-height: 40px !default;
/// height||Other|4
$--transfer-panel-body-height: 246px !default;
/// height||Other|4
$--transfer-item-height: 30px !default;
/// height||Other|4
$--transfer-filter-height: 32px !default;
/* Header
--------------------------*/
$--header-padding: 0 20px !default;
/* Footer
--------------------------*/
$--footer-padding: 0 20px !default;
/* Main
--------------------------*/
$--main-padding: 20px !default;
/* Timeline
--------------------------*/
$--timeline-node-size-normal: 12px !default;
$--timeline-node-size-large: 14px !default;
$--timeline-node-color: $--border-color-light !default;
/* Backtop
--------------------------*/
/// color||Color|0
$--backtop-background-color: $--color-white !default;
/// color||Color|0
$--backtop-font-color: $--color-primary !default;
/// color||Color|0
$--backtop-hover-background-color: $--border-color-extra-light !default;
/* Link
--------------------------*/
/// fontSize||Font|1
$--link-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--link-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--link-default-font-color: $--color-text-regular !default;
/// color||Color|0
$--link-default-active-color: $--color-primary !default;
/// color||Color|0
$--link-disabled-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--link-primary-font-color: $--color-primary !default;
/// color||Color|0
$--link-success-font-color: $--color-success !default;
/// color||Color|0
$--link-warning-font-color: $--color-warning !default;
/// color||Color|0
$--link-danger-font-color: $--color-danger !default;
/// color||Color|0
$--link-info-font-color: $--color-info !default;
/* Calendar
--------------------------*/
/// border||Other|4
$--calendar-border: $--table-border !default;
/// color||Other|4
$--calendar-selected-background-color: #F2F8FE !default;
$--calendar-cell-width: 85px !default;
/* Form
-------------------------- */
/// fontSize||Font|1
$--form-label-font-size: $--font-size-base !default;
/* Avatar
--------------------------*/
/// color||Color|0
$--avatar-font-color: #fff !default;
/// color||Color|0
$--avatar-background-color: #C0C4CC !default;
/// fontSize||Font Size|1
$--avatar-text-font-size: 14px !default;
/// fontSize||Font Size|1
$--avatar-icon-font-size: 18px !default;
/// borderRadius||Border|2
$--avatar-border-radius: $--border-radius-base !default;
/// size|1|Avatar Size|3
$--avatar-large-size: 40px !default;
/// size|1|Avatar Size|3
$--avatar-medium-size: 36px !default;
/// size|1|Avatar Size|3
$--avatar-small-size: 28px !default;
/* Break-point
--------------------------*/
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;
$--breakpoints: (
'xs' : (max-width: $--sm - 1),
'sm' : (min-width: $--sm),
'md' : (min-width: $--md),
'lg' : (min-width: $--lg),
'xl' : (min-width: $--xl)
);
$--breakpoints-spec: (
'xs-only' : (max-width: $--sm - 1),
'sm-and-up' : (min-width: $--sm),
'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
'sm-and-down': (max-width: $--md - 1),
'md-and-up' : (min-width: $--md),
'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
'md-and-down': (max-width: $--lg - 1),
'lg-and-up' : (min-width: $--lg),
'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
'lg-and-down': (max-width: $--xl - 1),
'xl-only' : (min-width: $--xl),
);

View File

@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -1,3 +0,0 @@
@import 'element-variables';
@import 'reboot';

View File

@@ -1,40 +0,0 @@
const title = 'Ratteb';
export const getPageTitle = (pageTitle) => {
if (pageTitle) {
return `${pageTitle} - ${title}`;
}
return title;
}
// const clipboardSuccess = () => {
// Vue.prototype.$message({
// message: 'Copy successfully',
// type: 'success',
// duration: 1500,
// });
// };
// const clipboardError = () => {
// Vue.prototype.$message({
// message: 'Copy failed',
// type: 'error',
// });
// };
export const handleClipboard = (text, event) => {
// const clipboard = new Clipboard(event.target, {
// text: () => text
// })
// clipboard.on('success', () => {
// clipboardSuccess()
// clipboard.destroy()
// })
// clipboard.on('error', () => {
// clipboardError()
// clipboard.destroy()
// })
// clipboard.onClick(event)
}

View File

@@ -1,2 +0,0 @@
export const set = property => (store, payload) => (store[property] = payload)
export const toggle = property => store => (store[property] = !store[property])

View File

@@ -1,14 +0,0 @@
<template>
<div></div>
</template>
<script>
export default {
name: 'search-dialog',
data() {
return {
current_state: 0,
};
},
};
</script>

Some files were not shown because too many files have changed in this diff Show More