├── .dockerignore ├── .editorconfig ├── .firebaserc ├── .github └── workflows │ └── main.yaml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json ├── settings.json └── vue3.code-snippets ├── Dockerfile ├── LICENSE ├── README.md ├── auto-imports.d.ts ├── default.env ├── firebase.json ├── index.html ├── nginx.conf ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ ├── css │ │ ├── el-breadcrumb.scss │ │ ├── el-button.scss │ │ ├── el-card.scss │ │ ├── el-loading.scss │ │ ├── el-menu.scss │ │ ├── el-pagination.scss │ │ ├── el-popper.scss │ │ ├── el-progress.scss │ │ ├── el-table.scss │ │ ├── el-tag.scss │ │ ├── element │ │ │ └── index.scss │ │ ├── form.scss │ │ ├── grid.scss │ │ ├── index.scss │ │ ├── notifications.scss │ │ ├── typography.scss │ │ └── var.scss │ └── images │ │ ├── John_Snow.png │ │ ├── angular.jpg │ │ ├── blue.png │ │ ├── bootstrap.jpg │ │ ├── dashboard.png │ │ ├── github.png │ │ ├── google.png │ │ ├── ltv_logo.png │ │ ├── man_draw.jpg │ │ ├── profile-cover.jpg │ │ ├── purple-woman-image.jpg │ │ ├── react.jpg │ │ ├── sketch.jpg │ │ ├── team-1.jpg │ │ ├── team-2.jpg │ │ ├── team-3.jpg │ │ ├── team-4.jpg │ │ ├── throne.jpg │ │ ├── tyrion_lannister.jpg │ │ ├── tywin_lannister.jpg │ │ ├── vue.jpg │ │ ├── white.png │ │ └── window-purple-image.jpg ├── components │ ├── BreadCrumb │ │ └── index.vue │ ├── ComponentLayout │ │ └── index.vue │ ├── Footer │ │ └── index.vue │ ├── Main │ │ └── index.vue │ ├── Navigation │ │ ├── AuthenticationNav.vue │ │ └── DefaultNav.vue │ ├── Pagination │ │ └── index.vue │ ├── SearchBar │ │ └── index.vue │ ├── Sidebar │ │ ├── SidebarNav.ts │ │ └── index.vue │ ├── element-plus.ts │ └── index.ts ├── core │ ├── constants.ts │ ├── emitter.ts │ └── env.ts ├── env.d.ts ├── global.d.ts ├── index.css ├── index.d.ts ├── layouts │ ├── auth-layout.vue │ └── default-layout.vue ├── main.ts ├── modules │ ├── auth │ │ ├── store │ │ │ ├── actions.ts │ │ │ ├── getters.ts │ │ │ ├── index.ts │ │ │ ├── state.ts │ │ │ └── types.ts │ │ └── views │ │ │ ├── components │ │ │ ├── ForgotPasswordForm.vue │ │ │ ├── LoginForm.vue │ │ │ ├── RegisterForm.vue │ │ │ ├── SplitBackground.vue │ │ │ └── WelcomeLabel.vue │ │ │ ├── forgot-password.vue │ │ │ ├── login.vue │ │ │ └── register.vue │ ├── buttons │ │ └── views │ │ │ ├── components │ │ │ ├── ColorButtons.vue │ │ │ ├── GroupButtons.vue │ │ │ ├── OutlineButtons.vue │ │ │ ├── SizeButtons.vue │ │ │ ├── SocialButtons.vue │ │ │ └── StyleButtons.vue │ │ │ └── index.vue │ ├── cards │ │ └── views │ │ │ ├── components │ │ │ ├── AnalysisCard.vue │ │ │ ├── ContactCard.vue │ │ │ ├── ImageFillCard.vue │ │ │ ├── ListGroupCard.vue │ │ │ ├── ProfileCard.vue │ │ │ ├── QuoteCard.vue │ │ │ ├── SimpleCard.vue │ │ │ └── TeamMemberCard.vue │ │ │ └── index.vue │ ├── dashboard │ │ ├── store │ │ │ ├── actions.ts │ │ │ ├── getters.ts │ │ │ ├── index.ts │ │ │ ├── state.ts │ │ │ └── types.ts │ │ └── views │ │ │ ├── components │ │ │ ├── GradientLineChart.vue │ │ │ ├── PageVisitTable.vue │ │ │ ├── SocialTrafficTable.vue │ │ │ └── TotalBarChart.vue │ │ │ └── index.vue │ ├── grid │ │ └── views │ │ │ ├── components │ │ │ ├── EqualWidth.vue │ │ │ ├── GridSystem.vue │ │ │ ├── Mix.vue │ │ │ ├── MultiRow.vue │ │ │ ├── OneColumnWidth.vue │ │ │ └── VariableWidth.vue │ │ │ └── index.vue │ ├── icons │ │ └── views │ │ │ └── index.vue │ ├── map │ │ └── views │ │ │ └── index.vue │ ├── notification │ │ └── views │ │ │ ├── components │ │ │ ├── Alerts.vue │ │ │ ├── DefaultModal.vue │ │ │ ├── Modals.vue │ │ │ ├── Notifications.vue │ │ │ └── WarningModal.vue │ │ │ └── index.vue │ ├── pages │ │ └── views │ │ │ └── 404.vue │ ├── profile │ │ └── views │ │ │ ├── components │ │ │ ├── EditProfileForm.vue │ │ │ ├── PrimaryIndicator.vue │ │ │ ├── ProgressTrack.vue │ │ │ └── WarningIndicator.vue │ │ │ └── index.vue │ ├── table │ │ └── views │ │ │ ├── components │ │ │ └── ProjectTable.vue │ │ │ └── index.vue │ ├── tags │ │ └── views │ │ │ └── index.vue │ └── typography │ │ └── views │ │ ├── components │ │ ├── DisplayTitle.vue │ │ ├── Heading.vue │ │ ├── Paragraphs.vue │ │ └── SpecializedTitle.vue │ │ └── index.vue ├── router │ ├── index.ts │ └── routes.ts ├── shims-element-plugins.d.ts ├── shims-element-plus.d.ts ├── shims-global.d.ts ├── shims-heroicons.d.ts ├── shims-router.d.ts ├── shims-vue.d.ts ├── store │ └── index.ts └── utils │ ├── extractStore.ts │ └── index.ts ├── tailwind.config.js ├── tools └── store │ ├── index │ └── templates │ ├── actions.ts.tpl │ ├── getters.ts.tpl │ ├── index.ts.tpl │ ├── mutations.ts.tpl │ ├── state.ts.tpl │ └── types.ts.tpl ├── tsconfig.json ├── vite.config.ts └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .browserslistrc 5 | .env 6 | .gitignore 7 | .prettierrc 8 | *.md 9 | cypress.json 10 | jest.config.js 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | insert_final_newline = true 7 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "ltv-project-template" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Build and push to Cloudfront on dev stage 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | deploy-cloudfront: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Cache node modules 13 | uses: actions/cache@v3 14 | with: 15 | path: ~/.yarn 16 | key: ${{ runner.os }}-build-source-${{ hashFiles('**/yarn.lock') }} 17 | restore-keys: | 18 | ${{ runner.os }}-build-source- 19 | ${{ runner.os }}-build- 20 | ${{ runner.os }}- 21 | 22 | - name: Create env 23 | run: | 24 | echo "VITE_APP_VERSION=${{ secrets.VITE_APP_VERSION }}" >> .env 25 | echo "VITE_GOOGLE_API_KEY=${{ secrets.VITE_GOOGLE_API_KEY }}" >> .env 26 | echo "VITE_DOCUMENT_ENDPOINT=${{ secrets.VITE_DOCUMENT_ENDPOINT }}" >> .env 27 | 28 | - name: Install dependencies 29 | run: yarn --cache-folder ~/.yarn --frozen-lockfile --check-files 30 | 31 | - name: Build source 32 | run: yarn build 33 | 34 | - name: Firebase Deploy 35 | uses: FirebaseExtended/action-hosting-deploy@v0 36 | with: 37 | repoToken: ${{ secrets.GITHUB_TOKEN }} 38 | firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }} 39 | target: argon-dashboard-vue3 40 | channelId: live 41 | projectId: ltv-project-template 42 | entryPoint: ./ 43 | 44 | - name: S3 and CloudFront Deploy 45 | run: | 46 | aws s3 sync ./dist s3://${{ secrets.S3_BUCKET_NAME }} 47 | env: 48 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 49 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 50 | AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} 51 | 52 | - name: Invalidate CloudFront 53 | uses: chetan/invalidate-cloudfront-action@v2 54 | env: 55 | DISTRIBUTION: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} 56 | PATHS: '/*' 57 | AWS_REGION: ${{ secrets.AWS_REGION }} 58 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 59 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .DS_Store 107 | dist-ssr 108 | *.local 109 | stats.html 110 | *.env.* 111 | !default.env 112 | components.d.ts 113 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "trailingComma": "all", 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "dbaeumer.vscode-eslint", 6 | "aaron-bond.better-comments", 7 | "streetsidesoftware.code-spell-checker", 8 | "mikestead.dotenv", 9 | "codezombiech.gitignore", 10 | "eamodio.gitlens", 11 | "wix.vscode-import-cost", 12 | "orta.vscode-jest", 13 | "yzhang.markdown-all-in-one", 14 | "davidanson.vscode-markdownlint", 15 | "christian-kohler.path-intellisense", 16 | "wayou.vscode-todo-highlight", 17 | "octref.vetur", 18 | "johnsoncodehk.volar", 19 | "hollowtree.vue-snippets", 20 | "prisma.prisma" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "cSpell.words": ["Srcs", "unstyled", "VITE"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/vue3.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "vue3": { 3 | "scope": "vue", 4 | "prefix": "vue3", 5 | "body": [ 6 | "", 9 | "", 10 | "", 18 | "" 19 | ], 20 | "description": "New Vue3 Single file" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine AS builder 2 | 3 | RUN apk --no-cache add \ 4 | g++ make python git \ 5 | && yarn global add node-gyp \ 6 | && rm -rf /var/cache/apk/* 7 | 8 | WORKDIR /builder/ 9 | 10 | # Cache frontend's package 11 | ADD package.json . 12 | ADD yarn.lock . 13 | 14 | # Install dependencies 15 | RUN yarn install --immutable --immutable-cache --check-cache 16 | 17 | # Cache frontend's src 18 | ADD . . 19 | 20 | # Build 21 | ADD prod.env .env 22 | 23 | RUN cat .env 24 | RUN yarn build --mode production 25 | 26 | # ===== Image ===== 27 | # ================== 28 | ## frontend Image 29 | FROM nginx:alpine AS frontend 30 | COPY nginx.conf /etc/nginx/nginx.conf 31 | COPY --from=builder /builder/dist/ /usr/share/nginx/html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 LTV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # argon-dashboard-vue3 2 | 3 | Argon Dashboard Vue 3, ElementUI & TailwindCSS - Inspired by [Creative Tim](https://www.creative-tim.com/product/vue-argon-dashboard) 4 | 5 | ![Hi](/src/assets/images/dashboard.png) 6 | 7 | ## Nice stack (VET) 8 | 9 | - [VueJS 3](https://vuejs.org) with well structured 10 | - [ElementUI](https://element-plus.org/en-US/) for VueJS 3 11 | - [TailwindCSS 3](https://tailwindcss.com) 12 | - [Vite](https://vitejs.dev) 13 | 14 | ## Sponsor 15 | 16 | LTV Software 17 | 18 | ![LTV Software](https://s.gravatar.com/avatar/bf6addc65b990260d9ba27bc1bee92b4?s=100) 19 | 20 | ## Contributors 21 | 22 | - [Dung Le](https://github.com/dzunglee) 23 | - [Phong Le](https://github.com/LeThanhPhongLTV) 24 | - [Hieu Dang](https://github.com/Trung-Hieu-Dev) 25 | - [Kha Huynh](https://github.com/khaht) 26 | 27 | ## License 28 | 29 | [MIT](https://opensource.org/licenses/MIT) 30 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | // We suggest you to commit this file into source control 3 | declare global { 4 | 5 | } 6 | export {} 7 | -------------------------------------------------------------------------------- /default.env: -------------------------------------------------------------------------------- 1 | # ALL VARIABLE MUST BE PREFIXED WITH `VITE_` 2 | VITE_APP_VERSION=0.1.0 3 | VITE_GOOGLE_API_KEY= 4 | VITE_DOCUMENT_ENDPOINT=https://argon-dashboard-vue3-docs.firebaseapp.com/ 5 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "site": "argon-dashboard-vue3", 5 | "public": "dist", 6 | "ignore": ["firebase.json", "**/.*", "**/*.map", "**/node_modules/**"], 7 | "rewrites": [ 8 | { 9 | "source": "**", 10 | "destination": "/index.html" 11 | } 12 | ] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | Argon Dashboard - Vue 3 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 34 | 35 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 15 | '$status $body_bytes_sent "$http_referer" ' 16 | '"$http_user_agent" "$http_x_forwarded_for"'; 17 | 18 | gzip on; 19 | sendfile on; 20 | tcp_nopush on; 21 | keepalive_timeout 65; 22 | access_log /var/log/nginx/access.log main; 23 | 24 | server { 25 | listen 80; 26 | 27 | root /usr/share/nginx/html/; 28 | index index.html; 29 | 30 | location ~ ^/(assets)/ { 31 | root /usr/share/nginx/html/; 32 | } 33 | 34 | location ~ \.(js|ico|html|txt)$ { 35 | root /usr/share/nginx/html/; 36 | } 37 | 38 | location ~ / { 39 | try_files /index.html = 404; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "halana-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vite --host 0.0.0.0", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview", 9 | "store": "node tools/store/index" 10 | }, 11 | "dependencies": { 12 | "@element-plus/icons-vue": "^1.1.4", 13 | "@fortawesome/fontawesome-svg-core": "^6.1.1", 14 | "@fortawesome/free-brands-svg-icons": "^6.1.1", 15 | "@fortawesome/free-solid-svg-icons": "^6.1.1", 16 | "@fortawesome/vue-fontawesome": "^3.0.0-5", 17 | "@headlessui/vue": "^1.3.0", 18 | "@heroicons/vue": "^1.0.1", 19 | "@sentry/tracing": "^6.16.1", 20 | "@sentry/vue": "^6.16.1", 21 | "axios": "^0.21.1", 22 | "buffer": "^6.0.3", 23 | "chart.js": "^3.8.0", 24 | "click-outside-vue3": "^4.0.1", 25 | "element-plus": "2.1.0", 26 | "emitter-io": "^1.39.0", 27 | "emoji-mart-vue-fast": "^10.2.1", 28 | "lodash": "^4.17.21", 29 | "mitt": "^3.0.0", 30 | "pinia": "^2.0.14", 31 | "vee-validate": "^4.4.7", 32 | "vue": "^3.2.27", 33 | "vue-chart-3": "^3.1.8", 34 | "vue-router": "^4.0.8", 35 | "vue-sweetalert2": "^5.0.5", 36 | "vue3-google-map": "^0.13.0", 37 | "vuex": "^4.0.2", 38 | "yup": "^0.32.9" 39 | }, 40 | "devDependencies": { 41 | "@tailwindcss/forms": "0.5.2", 42 | "@types/lodash": "^4.14.178", 43 | "@types/node": "^17.0.10", 44 | "@vitejs/plugin-vue": "^2.0.1", 45 | "@vue/compiler-sfc": "^3.2.27", 46 | "autoprefixer": "^10.2.5", 47 | "eslint": "^7.29.0", 48 | "eslint-config-prettier": "^8.3.0", 49 | "eslint-plugin-prettier": "^3.4.0", 50 | "fs-extra": "^10.0.0", 51 | "postcss": "^8.4.5", 52 | "prettier": "^2.5.1", 53 | "rollup-plugin-visualizer": "^5.5.0", 54 | "sass": "^1.49.0", 55 | "tailwindcss": "^3.0.15", 56 | "typescript": "^4.5.4", 57 | "unplugin-auto-import": "^0.5.11", 58 | "unplugin-vue-components": "^0.17.13", 59 | "vite": "^2.9.13", 60 | "vite-plugin-style-import": "^1.4.1", 61 | "vue-tsc": "^0.30.6", 62 | "yargs": "^17.0.1" 63 | }, 64 | "engines": { 65 | "node": ">=14" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 38 | 43 | -------------------------------------------------------------------------------- /src/assets/css/el-breadcrumb.scss: -------------------------------------------------------------------------------- 1 | .el-breadcrumb .el-breadcrumb__item .el-breadcrumb__separator { 2 | @apply font-medium mx-2; 3 | } -------------------------------------------------------------------------------- /src/assets/css/el-card.scss: -------------------------------------------------------------------------------- 1 | body { 2 | .el-card { 3 | @apply relative flex flex-col bg-clip-border break-words min-w-0 rounded-5 shadow-card cursor-auto text-primary-dark #{!important}; 4 | --el-card-bg-color: white !important; 5 | } 6 | .el-card .card-header { 7 | h3 { 8 | @apply mb-0 cursor-auto text-primary-dark; 9 | } 10 | } 11 | .el-card .el-card__header { 12 | @apply w-full py-5 px-6 mb-0 border-b-dark-4; 13 | } 14 | .el-card .el-card__body { 15 | @apply w-full py-6 px-6 grow shrink; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/css/el-loading.scss: -------------------------------------------------------------------------------- 1 | .el-loading-mask.is-fullscreen { 2 | @apply flex justify-center #{!important}; 3 | } 4 | .el-loading-mask .el-loading-spinner { 5 | @apply w-12 #{!important}; 6 | } -------------------------------------------------------------------------------- /src/assets/css/el-menu.scss: -------------------------------------------------------------------------------- 1 | .el-menu { 2 | .hidden-arrow { 3 | .el-sub-menu__title { 4 | .el-icon.el-sub-menu__icon-arrow { 5 | @apply hidden #{!important}; 6 | } 7 | } 8 | } 9 | .arrow-left { 10 | .el-sub-menu__title { 11 | .el-icon.el-sub-menu__icon-arrow { 12 | @apply absolute right-0 #{!important}; 13 | } 14 | } 15 | } 16 | .el-menu-item-group { 17 | .el-menu-item-group__title { 18 | @apply p-0; 19 | } 20 | .el-menu-item { 21 | @apply min-w-0 #{!important}; 22 | } 23 | } 24 | 25 | .el-sub-menu { 26 | .el-sub-menu__title { 27 | @apply rounded-md px-2 pr-0 h-11.25 text-dark-20 #{!important}; 28 | .el-icon.el-sub-menu__icon-arrow { 29 | @apply text-indigo-410; 30 | } 31 | &:hover { 32 | @apply bg-white; 33 | } 34 | } 35 | 36 | .el-menu-item { 37 | @apply text-dark-20; 38 | @apply pl-1.5 #{!important}; 39 | &:hover { 40 | @apply bg-white; 41 | } 42 | } 43 | } 44 | 45 | .el-menu-item { 46 | @apply rounded-md px-2 pr-0 h-11.25 text-dark-20 #{!important}; 47 | &:hover { 48 | @apply bg-white; 49 | } 50 | 51 | } 52 | .el-menu-item.is-active { 53 | @apply text-gray-800; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/assets/css/el-pagination.scss: -------------------------------------------------------------------------------- 1 | .el-pagination.is-background .number { 2 | @apply h-9 w-9 text-gray-500 font-normal rounded-full bg-transparent border border-slate-200 #{!important}; 3 | &:hover { 4 | @apply bg-slate-200 #{!important}; 5 | } 6 | } 7 | 8 | .el-pagination.is-background button { 9 | @apply w-9 h-9 text-gray-500 rounded-full font-bold bg-transparent border border-red-200 text-center #{!important}; 10 | &:hover { 11 | @apply bg-slate-200 #{!important}; 12 | } 13 | } 14 | 15 | .el-pagination.is-background button .el-icon { 16 | @apply font-extrabold border border-slate-200 rounded-full w-9 h-9 #{!important}; 17 | } 18 | 19 | .el-pagination.is-background button .el-icon .icon { 20 | @apply m-auto mt-2.75 #{!important}; 21 | } 22 | 23 | .el-pagination.is-background .el-pager .active { 24 | @apply bg-indigo-410 text-white drop-shadow-md #{!important}; 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/css/el-popper.scss: -------------------------------------------------------------------------------- 1 | .el-popper { 2 | @apply rounded-lg; 3 | } 4 | .el-dropdown-menu { 5 | @apply p-0; 6 | } 7 | 8 | .ava-column-popper.el-popover.el-popper { 9 | @apply p-1 min-w-fit rounded-md text-white bg-black border-slate-600 #{!important}; 10 | .el-popper__arrow::before { 11 | @apply bg-black #{!important}; 12 | } 13 | } 14 | .action-column-popper { 15 | @apply rounded-lg -mt-3 #{!important}; 16 | .el-popper__arrow { 17 | @apply hidden; 18 | } 19 | .el-dropdown-menu__item { 20 | @apply mx-0 text-zinc-800 #{!important}; 21 | &:hover { 22 | @apply bg-secondary; 23 | } 24 | } 25 | } 26 | 27 | .icon-popper.el-popover.el-popper { 28 | @apply transition-all duration-300 p-1 min-w-fit min-h-fit rounded-md #{!important}; 29 | .el-popper__arrow::before { 30 | @apply bg-white #{!important}; 31 | } 32 | } 33 | .auth-menu-popper.el-popper { 34 | @apply border shadow-card w-[94%] md:w-[95.5%] lg:hidden rounded-md p-6; 35 | @apply top-2.5 mx-2 inset-x-1 #{!important}; 36 | .el-dropdown-menu.el-dropdown-menu--default { 37 | @apply p-0; 38 | } 39 | .el-dropdown-menu__item--divided { 40 | @apply border-[#e4e4e5]; 41 | } 42 | .el-dropdown-menu__item { 43 | @apply p-0 m-0; 44 | &:hover { 45 | @apply bg-white; 46 | } 47 | } 48 | .el-popper__arrow { 49 | @apply hidden; 50 | } 51 | } 52 | 53 | .menu-popper { 54 | @apply lg:w-80 md:w-80 sm:w-80 w-97/100 border-none bg-[#182c4e] top-4 sm:top-1 rounded-lg #{!important}; 55 | .el-dropdown-menu__item--divided { 56 | @apply m-0; 57 | } 58 | } 59 | .notification-popper { 60 | @apply lg:w-98 md:w-98 sm:w-98 w-97/100 top-9/100 sm:top-16 rounded-lg #{!important}; 61 | .el-popper__arrow { 62 | @apply hidden; 63 | } 64 | .el-dropdown-menu { 65 | @apply p-0 #{!important}; 66 | } 67 | .el-dropdown-menu__item--divided { 68 | @apply m-0; 69 | } 70 | .el-dropdown-menu__item:first-child { 71 | @apply m-0 bg-white rounded-lg; 72 | &:hover { 73 | @apply bg-white #{!important}; 74 | } 75 | } 76 | .el-dropdown-menu__item { 77 | @apply m-0; 78 | &:hover { 79 | @apply bg-secondary #{!important}; 80 | } 81 | } 82 | } 83 | .profile-popper { 84 | @apply lg:w-48 md:w-48 sm:w-48 w-97/100 top-9/100 sm:top-18 rounded-lg #{!important}; 85 | .el-popper__arrow { 86 | @apply hidden; 87 | } 88 | .el-dropdown-menu { 89 | @apply py-2 #{!important}; 90 | .el-dropdown-menu__item:first-child { 91 | @apply m-0 rounded-lg; 92 | &:hover { 93 | @apply bg-white #{!important}; 94 | } 95 | } 96 | .el-dropdown-menu__item { 97 | @apply m-0; 98 | &:hover { 99 | @apply bg-secondary text-black #{!important}; 100 | } 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /src/assets/css/el-progress.scss: -------------------------------------------------------------------------------- 1 | .el-progress-bar .el-progress-bar__outer { 2 | @apply h-0.75 #{!important}; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/css/el-table.scss: -------------------------------------------------------------------------------- 1 | .is-light .el-table__header thead tr { 2 | @apply h-10; 3 | } 4 | 5 | .is-light .el-table__body .el-table__row { 6 | @apply h-13; 7 | } 8 | 9 | .is-light .el-table__header thead tr > th { 10 | @apply bg-slate-50 font-semibold px-4 py-2 whitespace-nowrap text-0.65 text-[#8898aa] #{!important}; 11 | } 12 | 13 | .is-light .el-table__body-wrapper tr td:first-child { 14 | @apply pl-4 #{!important}; 15 | } 16 | 17 | .is-dark .el-table__body .el-table__row { 18 | @apply text-white h-13 #{!important}; 19 | } 20 | 21 | .is-dark .el-table__header thead tr { 22 | @apply h-10; 23 | } 24 | 25 | .is-dark .el-table__header thead tr > th { 26 | @apply bg-dark-light font-semibold px-4 py-2 whitespace-nowrap text-0.65 text-[#4d7bca] #{!important}; 27 | } 28 | 29 | 30 | // Dark table style 31 | .is-dark .el-table__body-wrapper tr td:first-child { 32 | @apply pl-4 #{!important}; 33 | } 34 | 35 | .is-dark.el-table th, 36 | .is-dark.el-table tr { 37 | @apply bg-dark; 38 | } 39 | 40 | .is-dark .el-table__row { 41 | @apply border-dark-light #{!important}; 42 | } 43 | 44 | .is-dark .el-table__row:hover, 45 | .is-dark .el-table__row.hover-row { 46 | @apply bg-dark-light text-white #{!important}; 47 | } 48 | 49 | .is-dark.el-table th.is-leaf { 50 | @apply border-y-0.5 border-[#1f3a68] #{!important}; 51 | } 52 | 53 | .is-dark.el-table td, 54 | .is-dark.el-table th { 55 | @apply border-solid border-[#274a84] #{!important}; 56 | } 57 | 58 | 59 | 60 | .is-dark .el-table__body tr > td { 61 | @apply bg-dark text-white #{!important}; 62 | } 63 | 64 | .is-dark .el-table__body tr.hover-row.current-row > td, 65 | .is-dark .el-table__body tr.hover-row.el-table__row--striped.current-row > td, 66 | .is-dark .el-table__body tr.hover-row.el-table__row--striped > td, 67 | .is-dark .el-table__body tr.hover-row > td { 68 | @apply bg-dark-light text-white #{!important}; 69 | } 70 | -------------------------------------------------------------------------------- /src/assets/css/el-tag.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // effect dark 3 | .el-tag.el-tag--dark { 4 | @apply bg-primary border-primary text-white; 5 | .el-icon:hover { 6 | @apply text-primary bg-secondary; 7 | } 8 | } 9 | 10 | .el-tag.el-tag--dark.el-tag--warning { 11 | @apply bg-warning border-warning text-white; 12 | .el-icon:hover { 13 | @apply text-warning; 14 | } 15 | } 16 | 17 | .el-tag.el-tag--dark.el-tag--success { 18 | @apply bg-success border-success text-white; 19 | .el-icon:hover { 20 | @apply text-success ; 21 | } 22 | } 23 | 24 | .el-tag.el-tag--dark.el-tag--info { 25 | @apply bg-info border-info text-white; 26 | .el-icon:hover { 27 | @apply text-info ; 28 | } 29 | } 30 | 31 | .el-tag.el-tag--dark.el-tag--danger { 32 | @apply bg-danger border-danger text-white; 33 | .el-icon:hover { 34 | @apply text-danger ; 35 | } 36 | } 37 | 38 | // effect light 39 | .el-tag.el-tag--light { 40 | @apply bg-primary bg-opacity-20 text-primary; 41 | .el-icon { 42 | @apply text-primary; 43 | } 44 | 45 | .el-icon:hover { 46 | @apply bg-primary text-secondary text-opacity-50; 47 | } 48 | } 49 | 50 | .el-tag.el-tag--light.el-tag--warning { 51 | @apply bg-warning-50 bg-opacity-20 text-warning border-warning border-opacity-20; 52 | .el-icon { 53 | @apply text-warning; 54 | } 55 | 56 | .el-icon:hover { 57 | @apply text-secondary text-opacity-50 bg-warning; 58 | } 59 | } 60 | 61 | .el-tag.el-tag--light.el-tag--success { 62 | @apply bg-success-50 bg-opacity-20 text-success border-success border-opacity-20; 63 | .el-icon { 64 | @apply text-success; 65 | } 66 | 67 | .el-icon:hover { 68 | @apply text-secondary text-opacity-50 bg-success; 69 | } 70 | } 71 | 72 | .el-tag.el-tag--light.el-tag--info { 73 | @apply bg-info bg-opacity-20 text-info border-info border-opacity-20; 74 | .el-icon { 75 | @apply text-info; 76 | } 77 | 78 | .el-icon:hover { 79 | @apply text-secondary text-opacity-50 bg-info; 80 | } 81 | } 82 | 83 | .el-tag.el-tag--light.el-tag--danger { 84 | @apply bg-danger-50 bg-opacity-20 text-danger border-danger border-opacity-20; 85 | .el-icon { 86 | @apply text-danger; 87 | } 88 | 89 | .el-icon:hover { 90 | @apply text-secondary text-opacity-50 bg-danger; 91 | } 92 | } 93 | 94 | // effect plain 95 | .el-tag.el-tag--plain { 96 | @apply text-primary; 97 | .el-icon { 98 | @apply text-primary; 99 | } 100 | 101 | .el-icon:hover { 102 | @apply text-secondary bg-primary; 103 | } 104 | } 105 | 106 | .el-tag.el-tag--plain.el-tag--warning { 107 | @apply text-warning border-warning ; 108 | .el-icon { 109 | @apply text-warning; 110 | } 111 | 112 | .el-icon:hover { 113 | @apply text-secondary bg-warning; 114 | } 115 | } 116 | 117 | .el-tag.el-tag--plain.el-tag--success { 118 | @apply text-success border-success ; 119 | .el-icon { 120 | @apply text-success; 121 | } 122 | 123 | .el-icon:hover { 124 | @apply text-secondary bg-success; 125 | } 126 | } 127 | 128 | .el-tag.el-tag--plain.el-tag--info { 129 | @apply text-info border-info ; 130 | .el-icon { 131 | @apply text-info; 132 | } 133 | 134 | .el-icon:hover { 135 | @apply text-secondary bg-info; 136 | } 137 | } 138 | 139 | .el-tag.el-tag--plain.el-tag--danger { 140 | @apply text-danger border-danger ; 141 | .el-icon { 142 | @apply text-danger; 143 | } 144 | 145 | .el-icon:hover { 146 | @apply text-secondary bg-danger; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/assets/css/element/index.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/css/form.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // Default 3 | hr { 4 | @apply border-0 border-b border-b-light-10 my-6; 5 | } 6 | .el-form-item__content:last-child .el-button { 7 | @apply mt-6; 8 | } 9 | .el-input__inner { 10 | @apply h-11.5 rounded border border-light placeholder:text-muted text-sm; 11 | } 12 | .el-input-group__append { 13 | @apply text-base text-muted bg-white font-normal; 14 | } 15 | .el-upload .el-input__inner { 16 | @apply text-base font-medium placeholder:text-muted; 17 | } 18 | .el-input__inner:focus { 19 | @apply border outline-0 border-indigo-410 ring-0 shadow-[0_3px_9px_transparent] drop-shadow-[3px_4px_8px_rgb(94,114,228,0.10)]; 20 | } 21 | 22 | .el-upload.el-upload--text { 23 | @apply w-full #{!important}; 24 | } 25 | .el-form-item__label { 26 | @apply font-semibold; 27 | } 28 | .el-overlay { 29 | .el-overlay-dialog { 30 | @apply overflow-hidden #{!important}; 31 | } 32 | } 33 | // Notification - Form popup 34 | .el-dialog.sign-in { 35 | @apply rounded-md w-[90%] sm:w-95 sm:-top-10 #{!important}; 36 | 37 | .el-dialog__body { 38 | @apply flex flex-col items-center justify-center bg-secondary pt-0 rounded-md text-muted p-0 text-center; 39 | } 40 | .el-dialog__header { 41 | @apply hidden; 42 | } 43 | } 44 | 45 | // Shadow form 46 | .el-form.el-form--default.shadow-form { 47 | .el-form-item { 48 | @apply rounded; 49 | } 50 | .el-form-item__content .el-icon { 51 | @apply block flex-none text-lg leading-1.6 mx-2.5 #{!important}; 52 | } 53 | .el-input.el-input--default { 54 | @apply flex-1; 55 | } 56 | .el-form-item.el-form-item--default { 57 | @apply w-full border-0 p-0 drop-shadow-sm; 58 | } 59 | .el-input.el-input--default .el-input__inner { 60 | @apply h-11.5 p-0 text-sm rounded bg-white border-transparent placeholder:text-muted focus:text-muted focus:font-normal text-slate-400 focus:ring-0 focus:bg-white #{!important}; 61 | } 62 | .el-form-item__content { 63 | @apply justify-center; 64 | } 65 | 66 | .el-form-item.el-form-item--default.checkbox .el-form-item__content { 67 | @apply flex-none; 68 | } 69 | .el-checkbox__inner { 70 | @apply rounded h-4 w-4 border-0 shadow #{!important}; 71 | } 72 | .el-checkbox__label { 73 | @apply text-muted font-normal focus:border-transparent; 74 | } 75 | .el-checkbox__inner:hover { 76 | @apply border-inherit; 77 | } 78 | .el-checkbox__input.is-checked .el-checkbox__inner { 79 | @apply bg-indigo-410 border-0 #{!important}; 80 | } 81 | .el-checkbox__input.is-checked + .el-checkbox__label { 82 | @apply text-slate-400; 83 | } 84 | .el-button.el-button--default { 85 | @apply will-change-transform #{!important}; 86 | } 87 | .el-checkbox__inner::after { 88 | @apply border-2.5; 89 | } 90 | } 91 | .authentication-form { 92 | .el-form-item__error { 93 | @apply font-bold; 94 | } 95 | 96 | .el-input { 97 | .el-input__inner { 98 | @apply transition-all duration-300 rounded-md shadow-form-input border-none h-11.5 pl-10 text-muted text-sm placeholder:font-normal #{!important}; 99 | &:focus { 100 | @apply border-none shadow-md ring-0 #{!important}; 101 | } 102 | } 103 | } 104 | .el-checkbox { 105 | .el-checkbox__input { 106 | @apply shadow-lg; 107 | } 108 | .el-checkbox__inner { 109 | @apply rounded h-4 w-4 border-0 shadow #{!important}; 110 | } 111 | .el-checkbox__inner::after { 112 | @apply border-3 w-0.5 h-1.25 mt-0.5 ml-0.5 #{!important}; 113 | } 114 | } 115 | .el-checkbox.is-checked { 116 | .el-checkbox__input { 117 | .el-checkbox__inner { 118 | @apply bg-indigo-410 border-indigo-410; 119 | } 120 | } 121 | .el-checkbox__label { 122 | @apply text-muted focus:border-transparent; 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/assets/css/grid.scss: -------------------------------------------------------------------------------- 1 | body { 2 | .grid-items { 3 | @apply p-3 my-1 bg-white border-slate-200 rounded drop-shadow-[0_4px_16px_rgba(0,0,0,0.10)] shadow-[0_0_0_1px_rgb(0,0,0,0.10)] text-left text-sm text-gray-410 border-0 font-normal; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/css/index.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | overflow: hidden; 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/css/notifications.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // Overlay opacity 3 | .el-overlay { 4 | @apply bg-dark-10 #{!important}; 5 | } 6 | 7 | // Alerts 8 | .el-alert { 9 | @apply py-4 p-6 text-sm rounded-md; 10 | } 11 | .el-alert__closebtn { 12 | @apply top-2/4 right-6 text-lg translate-y-[-50%] text-slate-10 bg-transparent h-5 w-5 hover:bg-transparent hover:opacity-75 #{!important}; 13 | } 14 | .el-alert__title { 15 | @apply text-sm font-semibold leading-8; 16 | } 17 | // Alerts - Custom 18 | .el-alert.success { 19 | @apply bg-success; 20 | } 21 | .el-alert.info { 22 | @apply bg-cyan-310; 23 | } 24 | .el-alert.warning { 25 | @apply bg-warning-50; 26 | } 27 | .el-alert.danger { 28 | @apply bg-danger-50; 29 | } 30 | .el-alert.success, 31 | .el-alert.info, 32 | .el-alert.warning, 33 | .el-alert.danger { 34 | @apply text-white; 35 | } 36 | 37 | // Notifications 38 | .el-message { 39 | @apply py-4 pl-6 pr-20 min-w-0 w-[90%] max-w-1.5xl items-start rounded-md; 40 | } 41 | .el-message.default { 42 | @apply bg-dark-50 border-dark-50 text-white #{!important}; 43 | } 44 | .el-message .is-closable { 45 | @apply mb-4; 46 | } 47 | .el-message .el-message__closeBtn { 48 | @apply top-6 right-7 text-base font-bold #{!important}; 49 | } 50 | .el-message .message-header { 51 | @apply text-base font-semibold #{!important}; 52 | } 53 | .el-message .message-footer { 54 | @apply text-sm font-normal #{!important}; 55 | } 56 | .el-message .el-message-icon--success, 57 | .el-message .el-message-icon--info, 58 | .el-message .el-message-icon--warning, 59 | .el-message .el-message-icon--error { 60 | @apply text-xl mr-5 #{!important}; 61 | } 62 | 63 | // Notifications Custom 64 | .el-message.default .el-icon { 65 | @apply text-white #{!important}; 66 | } 67 | .el-message.is-closable.default .message-header, 68 | .el-message.is-closable.default .message-footer { 69 | @apply text-white; 70 | } 71 | 72 | .el-message.info { 73 | @apply bg-cyan-310 border-cyan-310 text-white #{!important}; 74 | } 75 | .el-message.info .el-icon { 76 | @apply text-white #{!important}; 77 | } 78 | .el-message.is-closable.info .message-header, 79 | .el-message.is-closable.info .message-footer { 80 | @apply text-white; 81 | } 82 | 83 | .el-message.success { 84 | @apply bg-success-50 border-success-50 text-white #{!important}; 85 | } 86 | .el-message.success .el-icon { 87 | @apply text-white #{!important}; 88 | } 89 | .el-message.is-closable.success .message-header, 90 | .el-message.is-closable.success .message-footer { 91 | @apply text-white; 92 | } 93 | 94 | .el-message.warning { 95 | @apply bg-warning-50 border-warning-50 text-white #{!important}; 96 | } 97 | .el-message.warning .el-icon { 98 | @apply text-white #{!important}; 99 | } 100 | .el-message.is-closable.warning .message-header, 101 | .el-message.is-closable.warning .message-footer { 102 | @apply text-white; 103 | } 104 | 105 | .el-message.danger { 106 | @apply bg-danger-50 border-danger-50 text-white #{!important}; 107 | } 108 | .el-message.danger .el-icon { 109 | @apply text-white #{!important}; 110 | } 111 | .el-message.is-closable.danger .message-header, 112 | .el-message.is-closable.danger .message-footer { 113 | @apply text-white; 114 | } 115 | 116 | // el-dialog - danger 117 | .el-dialog.danger { 118 | @apply bg-gradient-to-r from-danger to-red-light; 119 | .el-dialog__title, 120 | .el-dialog__body { 121 | @apply text-white break-normal #{!important}; 122 | } 123 | .el-dialog__body .el-icon { 124 | @apply text-5xl text-center #{!important}; 125 | } 126 | 127 | .el-button.el-button--danger { 128 | @apply bg-transparent text-white border-transparent shadow-none hover:translate-y-0 hover:border-transparent #{!important}; 129 | } 130 | } 131 | 132 | // el-dialog -default 133 | .el-dialog { 134 | @apply w-97/100 sm:w-8/12 md:w-7/12 lg:w-6/12 xl:w-[32.95%] rounded-md #{!important}; 135 | 136 | .el-button { 137 | @apply will-change-transform; 138 | } 139 | 140 | .el-dialog__header { 141 | @apply p-5 h-15; 142 | } 143 | .el-dialog__title { 144 | @apply inline-block text-1.0625 font-semibold h-[18.7px]; 145 | } 146 | .el-dialog__headerbtn .el-dialog__close { 147 | @apply h-5 w-5; 148 | } 149 | 150 | .el-dialog__body { 151 | @apply break-normal p-6 mb-4; 152 | } 153 | .el-dialog__body .el-icon { 154 | @apply text-base #{!important}; 155 | } 156 | .el-dialog__body p { 157 | @apply text-base leading-1.7; 158 | } 159 | 160 | .el-dialog__footer { 161 | @apply p-6; 162 | } 163 | .el-dialog__footer .dialog-footer { 164 | @apply w-full flex justify-between; 165 | } 166 | 167 | .el-button.el-button--secondary.is-plain { 168 | @apply bg-transparent text-indigo-410 hover:translate-y-0 border-transparent shadow-none hover:bg-transparent #{!important}; 169 | } 170 | 171 | .el-icon.el-dialog__close { 172 | @apply text-slate-300 hover:text-slate-200 #{!important}; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/assets/css/typography.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // Default 3 | h1, 4 | h2, 5 | h3, 6 | h4, 7 | h5, 8 | h6 { 9 | @apply text-primary-dark font-semibold leading-normal #{!important}; 10 | } 11 | h1 { 12 | @apply text-1.625; 13 | } 14 | h2 { 15 | @apply text-xl; 16 | } 17 | h3 { 18 | @apply text-1.0625; 19 | } 20 | h4 { 21 | @apply text-0.9375; 22 | } 23 | h5 { 24 | @apply text-0.8125; 25 | } 26 | h6 { 27 | @apply text-0.625; 28 | } 29 | abbr { 30 | @apply underline decoration-dotted text-base uppercase cursor-help; 31 | } 32 | 33 | // Customizing classes for H tags 34 | .h1, 35 | .h2, 36 | .h3, 37 | .h4, 38 | .h5, 39 | .h6 { 40 | @apply text-primary-dark font-semibold leading-normal #{!important}; 41 | } 42 | .h1 { 43 | @apply text-1.625; 44 | } 45 | .h2 { 46 | @apply text-xl; 47 | } 48 | .h3 { 49 | @apply text-1.0625; 50 | } 51 | .h4 { 52 | @apply text-0.9375; 53 | } 54 | .h5 { 55 | @apply text-0.8125; 56 | } 57 | .h6 { 58 | @apply text-0.625; 59 | } 60 | 61 | // Display-1 62 | .display-1, 63 | .display-2, 64 | .display-3, 65 | .display-4 { 66 | @apply text-primary-dark font-semibold break-all sm:break-normal; 67 | } 68 | .display-1 { 69 | @apply text-3.3; 70 | } 71 | .display-2 { 72 | @apply text-2.75; 73 | } 74 | .display-3 { 75 | @apply text-2.1875 break-normal #{!important}; 76 | } 77 | .display-4 { 78 | @apply text-1.6275; 79 | } 80 | 81 | // Specialized title 82 | .heading { 83 | @apply uppercase text-0.95 text-primary-dark font-semibold tracking-wide #{!important}; 84 | } 85 | .heading-title { 86 | @apply uppercase text-1.375 text-primary-dark font-semibold tracking-wide; 87 | } 88 | .lead { 89 | @apply text-xl font-light leading-1.7; 90 | } 91 | .mark { 92 | @apply bg-amber-60 p-[0.2rem]; 93 | } 94 | .small { 95 | @apply text-[0.8rem] font-normal; 96 | } 97 | .initialism { 98 | @apply text-[0.9rem]; 99 | } 100 | 101 | // Paragraphs 102 | p { 103 | @apply text-dark-lighter text-base font-light leading-1.7; 104 | } 105 | .blockquote { 106 | @apply mb-4 text-xl text-dark-lighter font-normal leading-normal; 107 | } 108 | .blockquote-footer { 109 | @apply text-muted text-80 font-normal leading-normal; 110 | } 111 | // .text-primary { 112 | // @apply text-indigo-410 #{!important}; 113 | // } 114 | 115 | // List 116 | ul { 117 | @apply list-disc pl-10; 118 | } 119 | .list-unstyled { 120 | @apply pl-0 list-none; 121 | } 122 | .list-inline { 123 | @apply mb-4; 124 | } 125 | .list-inline-item { 126 | @apply mr-2 inline-block; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/assets/css/var.scss: -------------------------------------------------------------------------------- 1 | $primary-color: red; 2 | -------------------------------------------------------------------------------- /src/assets/images/John_Snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/John_Snow.png -------------------------------------------------------------------------------- /src/assets/images/angular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/angular.jpg -------------------------------------------------------------------------------- /src/assets/images/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/blue.png -------------------------------------------------------------------------------- /src/assets/images/bootstrap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/bootstrap.jpg -------------------------------------------------------------------------------- /src/assets/images/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/dashboard.png -------------------------------------------------------------------------------- /src/assets/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/github.png -------------------------------------------------------------------------------- /src/assets/images/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/google.png -------------------------------------------------------------------------------- /src/assets/images/ltv_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/ltv_logo.png -------------------------------------------------------------------------------- /src/assets/images/man_draw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/man_draw.jpg -------------------------------------------------------------------------------- /src/assets/images/profile-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/profile-cover.jpg -------------------------------------------------------------------------------- /src/assets/images/purple-woman-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/purple-woman-image.jpg -------------------------------------------------------------------------------- /src/assets/images/react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/react.jpg -------------------------------------------------------------------------------- /src/assets/images/sketch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/sketch.jpg -------------------------------------------------------------------------------- /src/assets/images/team-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/team-1.jpg -------------------------------------------------------------------------------- /src/assets/images/team-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/team-2.jpg -------------------------------------------------------------------------------- /src/assets/images/team-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/team-3.jpg -------------------------------------------------------------------------------- /src/assets/images/team-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/team-4.jpg -------------------------------------------------------------------------------- /src/assets/images/throne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/throne.jpg -------------------------------------------------------------------------------- /src/assets/images/tyrion_lannister.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/tyrion_lannister.jpg -------------------------------------------------------------------------------- /src/assets/images/tywin_lannister.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/tywin_lannister.jpg -------------------------------------------------------------------------------- /src/assets/images/vue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/vue.jpg -------------------------------------------------------------------------------- /src/assets/images/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/white.png -------------------------------------------------------------------------------- /src/assets/images/window-purple-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltv/argon-dashboard-vue3/dfc040bda79d0408f0dbc37d4faa53e2dc1a9450/src/assets/images/window-purple-image.jpg -------------------------------------------------------------------------------- /src/components/BreadCrumb/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 56 | -------------------------------------------------------------------------------- /src/components/ComponentLayout/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/Footer/index.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 70 | -------------------------------------------------------------------------------- /src/components/Main/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /src/components/Navigation/AuthenticationNav.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | 135 | -------------------------------------------------------------------------------- /src/components/Pagination/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /src/components/SearchBar/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 49 | 50 | 61 | -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarNav.ts: -------------------------------------------------------------------------------- 1 | import routes from 'router/routes' 2 | 3 | const navigation = () => { 4 | return routes.reduce((prev: any, curr) => { 5 | const { meta, path, name, children } = curr 6 | if (meta.icon) { 7 | prev.push({ name, href: path, icon: meta.icon, color: meta.color, title: meta.title, children: children, requiresAuth: meta.requiresAuth }) 8 | } 9 | return prev 10 | }, []) 11 | } 12 | 13 | export default navigation() 14 | -------------------------------------------------------------------------------- /src/components/element-plus.ts: -------------------------------------------------------------------------------- 1 | // Custom css, do it later, now use the element plus css 2 | import 'assets/css/el-button.scss' 3 | import 'assets/css/typography.scss' 4 | import 'assets/css/el-popper.scss' 5 | import 'assets/css/el-breadcrumb.scss' 6 | import 'assets/css/notifications.scss' 7 | import 'assets/css/form.scss' 8 | import 'assets/css/grid.scss' 9 | 10 | import 'assets/css/el-progress.scss' 11 | import 'assets/css/el-loading.scss' 12 | import 'assets/css/el-menu.scss' 13 | import 'assets/css/el-card.scss' 14 | import 'assets/css/el-pagination.scss' 15 | import 'assets/css/el-table.scss' 16 | import 'assets/css/el-tag.scss' 17 | import 'element-plus/dist/index.css' 18 | import 'element-plus/es/components/message/style/css' 19 | import 'element-plus/es/components/message-box/style/css' 20 | import { App } from 'vue' 21 | 22 | export const useElementPlus = (app: App) => { 23 | app.provide('$message', app.config.globalProperties.$message) 24 | app.provide('$messageBox', app.config.globalProperties.$messageBox) 25 | } 26 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | import 'assets/css/index.scss' 2 | import { App } from 'vue' 3 | import { useElementPlus } from './element-plus' 4 | import Main from './Main/index.vue' 5 | import Sidebar from './Sidebar/index.vue' 6 | import Navigation from './Navigation/DefaultNav.vue' 7 | import AuthNavigation from './Navigation/AuthenticationNav.vue' 8 | import Footer from './Footer/index.vue' 9 | import BreadCrumb from './BreadCrumb/index.vue' 10 | import SearchBar from './SearchBar/index.vue' 11 | import Pagination from './Pagination/index.vue' 12 | 13 | export default { 14 | install: (app: App) => { 15 | // Register it globally 16 | app.component('Main', Main) 17 | app.component('Sidebar', Sidebar) 18 | app.component('Navigation', Navigation) 19 | app.component('AuthNavigation', AuthNavigation) 20 | app.component('Footer', Footer) 21 | app.component('BreadCrumb', BreadCrumb) 22 | app.component('SearchBar', SearchBar) 23 | app.component('Pagination', Pagination) 24 | 25 | // Element Plus 26 | useElementPlus(app) 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /src/core/constants.ts: -------------------------------------------------------------------------------- 1 | import env from 'core/env' 2 | export const PAGE_SIZE = env('VITE_PAGE_SIZE', 10) 3 | export const AUTH_TOKEN = 'app/token' 4 | -------------------------------------------------------------------------------- /src/core/emitter.ts: -------------------------------------------------------------------------------- 1 | import mitt from 'mitt' 2 | 3 | const emitter = mitt() 4 | export default emitter 5 | -------------------------------------------------------------------------------- /src/core/env.ts: -------------------------------------------------------------------------------- 1 | import has from 'lodash/has' 2 | import trim from 'lodash/trim' 3 | 4 | const metaEnv: any = import.meta.env 5 | 6 | type EnvFunc = (key: string, defaultValue?: T) => T 7 | type Utils = { 8 | string: (key: string, defaultValue?: string) => string | undefined 9 | int: (key: string, defaultValue?: number) => number | undefined 10 | float: (key: string, defaultValue?: number) => number | undefined 11 | bool: (key: string, defaultValue?: boolean) => boolean | undefined 12 | json: (key: string, defaultValue?: T) => T | undefined 13 | array: (key: string, defaultValue?: string[]) => string[] | undefined 14 | date: (key: string, defaultValue?: Date) => Date | undefined 15 | } 16 | 17 | const utils: Utils = { 18 | /** 19 | * Get string from environment 20 | * 21 | * @param key string 22 | * @param defaultValue string 23 | * @returns string 24 | */ 25 | string(key: string, defaultValue?: string): string | undefined { 26 | return has(metaEnv, key) ? metaEnv[key] : defaultValue 27 | }, 28 | 29 | /** 30 | * Get integer from environment 31 | * 32 | * @param key string 33 | * @param defaultValue integer number 34 | * @returns integer number 35 | */ 36 | int(key: string, defaultValue?: number): number | undefined { 37 | if (!has(metaEnv, key)) { 38 | return defaultValue 39 | } 40 | 41 | const value = metaEnv[key] || '' 42 | return parseInt(value, 10) 43 | }, 44 | 45 | /** 46 | * Get float from environment 47 | * 48 | * @param key string 49 | * @param defaultValue float number 50 | * @returns float number 51 | */ 52 | float(key: string, defaultValue?: number): number | undefined { 53 | if (!has(metaEnv, key)) { 54 | return defaultValue 55 | } 56 | 57 | const value = metaEnv[key] || '' 58 | return parseFloat(value) 59 | }, 60 | 61 | /** 62 | * Get boolean from environment 63 | * 64 | * @param key string 65 | * @param defaultValue boolean 66 | * @returns boolean 67 | */ 68 | bool(key: string, defaultValue?: boolean): boolean | undefined { 69 | if (!has(metaEnv, key)) { 70 | return defaultValue 71 | } 72 | 73 | const value = metaEnv[key] 74 | return value === 'true' 75 | }, 76 | 77 | /** 78 | * Get JSON object from environment 79 | * 80 | * @param key string 81 | * @param defaultValue string 82 | * @returns object 83 | */ 84 | json(key: string, defaultValue?: T): T | undefined { 85 | if (!has(metaEnv, key)) { 86 | return defaultValue 87 | } 88 | 89 | const value = metaEnv[key] || '' 90 | try { 91 | return JSON.parse(value) 92 | } catch (error: any) { 93 | throw new Error( 94 | `Invalid json environment variable ${key}: ${error.message}`, 95 | ) 96 | } 97 | }, 98 | 99 | /** 100 | * Get string array from environment 101 | * 102 | * @param key string 103 | * @param defaultValue string[] 104 | * @returns string[] 105 | */ 106 | array(key: string, defaultValue?: string[]): string[] | undefined { 107 | if (!has(metaEnv, key)) { 108 | return defaultValue 109 | } 110 | 111 | let value = metaEnv[key] || '' 112 | 113 | if (value.startsWith('[') && value.endsWith(']')) { 114 | value = value.substring(1, value.length - 1) 115 | } 116 | 117 | return value.split(',').map((v: string) => { 118 | return trim(trim(v, ' '), '"') 119 | }) 120 | }, 121 | 122 | /** 123 | * Get Date object from environment 124 | * 125 | * @param key string 126 | * @param defaultValue Date 127 | * @returns Date 128 | */ 129 | date(key: string, defaultValue?: Date): Date | undefined { 130 | if (!has(metaEnv, key)) { 131 | return defaultValue 132 | } 133 | 134 | const value = metaEnv[key] || '' 135 | return new Date(value) 136 | }, 137 | } 138 | 139 | /** 140 | * Get data from environment 141 | * 142 | * @param key string 143 | * @param defaultValue 144 | * @returns T 145 | */ 146 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 147 | function env(key: string, defaultValue?: any) { 148 | return has(metaEnv, key) ? metaEnv[key] : defaultValue 149 | } 150 | 151 | export default Object.assign(env, utils) 152 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | SERVICE_ENDPOINT: string 3 | } 4 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | interface ElementForm { 2 | validate: (callback?: Callback) => Promise 3 | resetFields: () => void 4 | clearValidate: (props?: string | string[]) => void 5 | validateField: (props: string | string[], cb: ValidateFieldCallback) => void 6 | } 7 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | * { 7 | font-family: 'Open Sans', sans-serif; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | interface IMessageHandle { 2 | close: () => void 3 | } 4 | type IMessageOptions = { 5 | customClass?: string 6 | center?: boolean 7 | dangerouslyUseHTMLString?: boolean 8 | duration?: number 9 | iconClass?: string 10 | id?: string 11 | message?: string | VNode 12 | offset?: number 13 | onClose?: () => void 14 | showClose?: boolean 15 | type?: 'success' | 'warning' | 'info' | 'error' | '' 16 | zIndex?: number 17 | } 18 | type MessageType = 'success' | 'warning' | 'info' | 'error' | '' 19 | type IMessageDispatcher = (options?: IMessageOptions | string) => IMessageHandle 20 | type MessageParams = IMessageOptions | string 21 | type TypedMessageParams = 22 | | ({ 23 | type: T 24 | } & Omit) 25 | | string 26 | 27 | interface IMessage { 28 | (options?: MessageParams): IMessageHandle 29 | success: (options?: TypedMessageParams<'success'>) => IMessageHandle 30 | warning: (options?: TypedMessageParams<'warning'>) => IMessageHandle 31 | info: (options?: TypedMessageParams<'info'>) => IMessageHandle 32 | error: (options?: TypedMessageParams<'error'>) => IMessageHandle 33 | closeAll(): void 34 | } 35 | 36 | interface RouterAbility { 37 | name: string 38 | title: string 39 | } 40 | -------------------------------------------------------------------------------- /src/layouts/auth-layout.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 37 | -------------------------------------------------------------------------------- /src/layouts/default-layout.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 87 | 88 | 98 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import vueEmitter from 'core/emitter' 2 | import { createApp, h } from 'vue' 3 | import { createPinia } from 'pinia' 4 | import ElementPlus from 'element-plus' 5 | import App from './App.vue' 6 | import AppComponents from './components' 7 | import VueSweetAlert2 from 'vue-sweetalert2' 8 | 9 | import { library } from '@fortawesome/fontawesome-svg-core' 10 | import { fas } from '@fortawesome/free-solid-svg-icons' 11 | import { fab } from '@fortawesome/free-brands-svg-icons' 12 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' 13 | 14 | import 'sweetalert2/dist/sweetalert2.min.css' 15 | 16 | import './index.css' 17 | import router from './router' 18 | // create new app instance 19 | const createNewApp = () => { 20 | const app = createApp({ 21 | render: () => h(App), 22 | }) 23 | library.add(fas, fab) 24 | 25 | app.component('font-awesome-icon', FontAwesomeIcon) 26 | app.provide('eventHub', vueEmitter) 27 | app.use(router) 28 | app.use(ElementPlus) 29 | app.use(AppComponents) 30 | app.use(createPinia()) 31 | app.use(VueSweetAlert2) 32 | 33 | app.mount('#app') 34 | app.config.performance = true 35 | } 36 | 37 | const initApp = async () => { 38 | createNewApp() 39 | } 40 | 41 | initApp().then(() => { 42 | // initialized 43 | }) 44 | -------------------------------------------------------------------------------- /src/modules/auth/store/actions.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { ILogin } from './types' 3 | import { AUTH_TOKEN } from 'core/constants' 4 | 5 | 6 | export const useActions = defineStore('auth.actions', () => { 7 | const actLogin = async (_: ILogin) => { 8 | localStorage.setItem(AUTH_TOKEN, 'user') 9 | window.location.href = '/' 10 | } 11 | const actLogout = () => { 12 | localStorage.removeItem(AUTH_TOKEN) 13 | window.location.href = '/login' 14 | } 15 | 16 | return { 17 | actLogin, 18 | actLogout, 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /src/modules/auth/store/getters.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | import { computed } from 'vue' 4 | import { useState } from './state' 5 | 6 | export const useGetters = defineStore('auth.getters', () => { 7 | const state = useState() 8 | const getAuthenticationState = computed((): boolean => state.isAuthenticated) 9 | const getuser = computed((): any => state.user) 10 | return { getAuthenticationState, getuser } 11 | }) 12 | -------------------------------------------------------------------------------- /src/modules/auth/store/index.ts: -------------------------------------------------------------------------------- 1 | import { extractStore } from 'utils/extractStore' 2 | import { defineStore } from 'pinia' 3 | import { useActions } from './actions' 4 | import { useGetters } from './getters' 5 | import { useState } from './state' 6 | 7 | export const useAuthStore = defineStore('auth', () => { 8 | return { 9 | ...extractStore(useState()), 10 | ...extractStore(useGetters()), 11 | ...extractStore(useActions()), 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/modules/auth/store/state.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { AuthState } from './types' 3 | import { AUTH_TOKEN } from 'core/constants' 4 | 5 | 6 | export const useState = defineStore({ 7 | id: 'auth.state', 8 | state: (): AuthState => { 9 | return { 10 | isAuthenticated: !!localStorage.getItem(AUTH_TOKEN), 11 | } 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /src/modules/auth/store/types.ts: -------------------------------------------------------------------------------- 1 | export interface AuthState { 2 | isAuthenticated: boolean 3 | user?: any 4 | } 5 | 6 | export type ILogin = { 7 | email: string 8 | password: string 9 | } 10 | -------------------------------------------------------------------------------- /src/modules/auth/views/components/ForgotPasswordForm.vue: -------------------------------------------------------------------------------- 1 | 25 | 44 | -------------------------------------------------------------------------------- /src/modules/auth/views/components/LoginForm.vue: -------------------------------------------------------------------------------- 1 | 49 | 108 | -------------------------------------------------------------------------------- /src/modules/auth/views/components/SplitBackground.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /src/modules/auth/views/components/WelcomeLabel.vue: -------------------------------------------------------------------------------- 1 | 10 | 17 | -------------------------------------------------------------------------------- /src/modules/auth/views/forgot-password.vue: -------------------------------------------------------------------------------- 1 | 29 | 51 | -------------------------------------------------------------------------------- /src/modules/auth/views/login.vue: -------------------------------------------------------------------------------- 1 | 57 | 81 | -------------------------------------------------------------------------------- /src/modules/auth/views/register.vue: -------------------------------------------------------------------------------- 1 | 29 | 51 | -------------------------------------------------------------------------------- /src/modules/buttons/views/components/ColorButtons.vue: -------------------------------------------------------------------------------- 1 | 28 | 35 | -------------------------------------------------------------------------------- /src/modules/buttons/views/components/GroupButtons.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 38 | 39 | 46 | -------------------------------------------------------------------------------- /src/modules/buttons/views/components/OutlineButtons.vue: -------------------------------------------------------------------------------- 1 | 28 | 35 | -------------------------------------------------------------------------------- /src/modules/buttons/views/components/SizeButtons.vue: -------------------------------------------------------------------------------- 1 | 29 | 36 | -------------------------------------------------------------------------------- /src/modules/buttons/views/components/StyleButtons.vue: -------------------------------------------------------------------------------- 1 | 22 | 34 | -------------------------------------------------------------------------------- /src/modules/buttons/views/index.vue: -------------------------------------------------------------------------------- 1 | 59 | 79 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/AnalysisCard.vue: -------------------------------------------------------------------------------- 1 | 116 | 130 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/ContactCard.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 61 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/ImageFillCard.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 41 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/ListGroupCard.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 38 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/ProfileCard.vue: -------------------------------------------------------------------------------- 1 | 79 | 139 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/QuoteCard.vue: -------------------------------------------------------------------------------- 1 | 21 | 42 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/SimpleCard.vue: -------------------------------------------------------------------------------- 1 | 18 | 31 | -------------------------------------------------------------------------------- /src/modules/cards/views/components/TeamMemberCard.vue: -------------------------------------------------------------------------------- 1 | 43 | 69 | -------------------------------------------------------------------------------- /src/modules/dashboard/store/actions.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { useState } from './state' 3 | import { useGetters } from './getters' 4 | 5 | export const useActions = defineStore('dashboard.actions', () => { 6 | const state = useState() 7 | const getters = useGetters() 8 | 9 | const showWelcomeText = () => { 10 | console.log('getters value', getters.getText) 11 | if (state.welcomeText) { 12 | console.log(state.welcomeText) 13 | } 14 | } 15 | 16 | const setWelcomeText = (value = '') => { 17 | state.welcomeText = value 18 | } 19 | 20 | const setIsSBPin = (value: boolean) => (state.isSBPin = value) 21 | const setIsSBOpen = (value: boolean) => (state.isSBOpen = value) 22 | 23 | const toggleMenu = () => { 24 | if (window.innerWidth < 1024) { 25 | setIsSBOpen(!state.isSBOpen) 26 | } else { 27 | setIsSBPin(!state.isSBPin) 28 | } 29 | } 30 | 31 | 32 | return { 33 | showWelcomeText, 34 | setWelcomeText, 35 | setIsSBPin, 36 | setIsSBOpen, 37 | toggleMenu, 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /src/modules/dashboard/store/getters.ts: -------------------------------------------------------------------------------- 1 | import { defineStore, StoreGetters } from 'pinia' 2 | import { DashboardState } from './types' 3 | 4 | export const getters: StoreGetters = { 5 | welcomeTextText: (state: DashboardState) => 6 | state.welcomeText ? 'Welcome' : 'Goodbye', 7 | } 8 | import { computed } from 'vue' 9 | import { useState } from './state' 10 | 11 | export const useGetters = defineStore('dashboard.getters', () => { 12 | const state = useState() 13 | const getText = computed((): string => `foo-${state.welcomeText.toString()}`) 14 | 15 | return { 16 | getText, 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /src/modules/dashboard/store/index.ts: -------------------------------------------------------------------------------- 1 | import { extractStore } from 'utils/extractStore' 2 | import { defineStore } from 'pinia' 3 | import { useActions } from './actions' 4 | import { useGetters } from './getters' 5 | import { useState } from './state' 6 | 7 | export const useDashboardStore = defineStore('dashboard', () => { 8 | return { 9 | ...extractStore(useState()), 10 | ...extractStore(useGetters()), 11 | ...extractStore(useActions()), 12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/modules/dashboard/store/state.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { DashboardState } from './types' 3 | 4 | export const useState = defineStore({ 5 | id: 'dashboard.state', 6 | state: (): DashboardState => { 7 | return { 8 | welcomeText: 'Welcome to Dashboard! ...', 9 | isSBPin: true, 10 | isSBOpen: true, 11 | } 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /src/modules/dashboard/store/types.ts: -------------------------------------------------------------------------------- 1 | export interface DashboardState { 2 | welcomeText: string 3 | isSBPin: boolean 4 | isSBOpen: boolean 5 | } 6 | -------------------------------------------------------------------------------- /src/modules/dashboard/views/components/GradientLineChart.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 172 | -------------------------------------------------------------------------------- /src/modules/dashboard/views/components/PageVisitTable.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 130 | -------------------------------------------------------------------------------- /src/modules/dashboard/views/components/SocialTrafficTable.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 116 | -------------------------------------------------------------------------------- /src/modules/dashboard/views/components/TotalBarChart.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 140 | -------------------------------------------------------------------------------- /src/modules/dashboard/views/index.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 56 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/EqualWidth.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/GridSystem.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/Mix.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/MultiRow.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/OneColumnWidth.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /src/modules/grid/views/components/VariableWidth.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /src/modules/grid/views/index.vue: -------------------------------------------------------------------------------- 1 | 64 | 88 | 89 | 94 | -------------------------------------------------------------------------------- /src/modules/icons/views/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 44 | 45 | 105 | -------------------------------------------------------------------------------- /src/modules/map/views/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /src/modules/notification/views/components/Alerts.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/modules/notification/views/components/DefaultModal.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 42 | -------------------------------------------------------------------------------- /src/modules/notification/views/components/Modals.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 83 | -------------------------------------------------------------------------------- /src/modules/notification/views/components/Notifications.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 134 | -------------------------------------------------------------------------------- /src/modules/notification/views/components/WarningModal.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 47 | -------------------------------------------------------------------------------- /src/modules/notification/views/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 50 | -------------------------------------------------------------------------------- /src/modules/pages/views/404.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 42 | -------------------------------------------------------------------------------- /src/modules/profile/views/components/EditProfileForm.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 104 | 105 | 116 | -------------------------------------------------------------------------------- /src/modules/profile/views/components/PrimaryIndicator.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 39 | -------------------------------------------------------------------------------- /src/modules/profile/views/components/ProgressTrack.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 145 | 146 | 158 | -------------------------------------------------------------------------------- /src/modules/profile/views/components/WarningIndicator.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 39 | -------------------------------------------------------------------------------- /src/modules/profile/views/index.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 104 | 105 | 113 | -------------------------------------------------------------------------------- /src/modules/table/views/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 120 | -------------------------------------------------------------------------------- /src/modules/tags/views/index.vue: -------------------------------------------------------------------------------- 1 | 36 | 43 | -------------------------------------------------------------------------------- /src/modules/typography/views/components/DisplayTitle.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 49 | 50 | 64 | -------------------------------------------------------------------------------- /src/modules/typography/views/components/Heading.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 67 | 68 | 82 | -------------------------------------------------------------------------------- /src/modules/typography/views/components/Paragraphs.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | 130 | 131 | 145 | -------------------------------------------------------------------------------- /src/modules/typography/views/components/SpecializedTitle.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 48 | 49 | 63 | -------------------------------------------------------------------------------- /src/modules/typography/views/index.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 61 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import useStore from 'store' 2 | import { createRouter, createWebHistory, Router } from 'vue-router' 3 | import routes from './routes' 4 | 5 | const router: Router = createRouter({ 6 | history: createWebHistory(), 7 | routes, 8 | }) 9 | 10 | router.beforeEach(() => { 11 | const store = useStore() 12 | store.dashboard.setIsSBOpen(false) 13 | }) 14 | 15 | export default router 16 | -------------------------------------------------------------------------------- /src/router/routes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ViewGridIcon, 3 | CursorClickIcon, 4 | DocumentTextIcon, 5 | StarIcon, 6 | HomeIcon, 7 | ViewBoardsIcon, 8 | BellIcon, 9 | LocationMarkerIcon, 10 | UserIcon, 11 | ColorSwatchIcon 12 | } from '@heroicons/vue/outline' 13 | 14 | import { 15 | CreditCardIcon, 16 | } from '@heroicons/vue/solid' 17 | 18 | const Login = () => import('modules/auth/views/login.vue') 19 | const Register = () => import('modules/auth/views/register.vue') 20 | const ForgotPassword = () => import('modules/auth/views/forgot-password.vue') 21 | const NotFound = () => import('modules/pages/views/404.vue') 22 | const Dashboard = () => import('modules/dashboard/views/index.vue') 23 | const Table = () => import('modules/table/views/index.vue') 24 | const Grid = () => import('modules/grid/views/index.vue') 25 | const Notification = () => import('modules/notification/views/index.vue') 26 | const Button = () => import('modules/buttons/views/index.vue') 27 | const Tags = () => import('modules/tags/views/index.vue') 28 | const Typography = () => import('modules/typography/views/index.vue') 29 | const Card = () => import('modules/cards/views/index.vue') 30 | const Icons = () => import('modules/icons/views/index.vue') 31 | const Profile = () => import('modules/profile/views/index.vue') 32 | const Map = () => import('modules/map/views/index.vue') 33 | 34 | const ComponentLayout = () => import('components/ComponentLayout/index.vue') 35 | 36 | 37 | const routes = [ 38 | { 39 | path: '/', 40 | component: Dashboard, 41 | name: 'Dashboard', 42 | meta: { 43 | title: 'Dashboard', 44 | icon: HomeIcon, 45 | color: 'text-indigo-410', 46 | requiresAuth: true, 47 | parentPath: 'Home' 48 | }, 49 | }, 50 | { 51 | path: '/components/:componentItem?', 52 | component: ComponentLayout, 53 | name: 'Components', 54 | meta: { 55 | title: 'Components', 56 | icon: ColorSwatchIcon, 57 | color: 'text-info', 58 | requiresAuth: true, 59 | parentPath: 'Components' 60 | }, 61 | children: [ 62 | { 63 | path: 'buttons', 64 | name: 'Buttons', 65 | component: Button, 66 | meta: { 67 | title: 'Buttons', 68 | icon: CursorClickIcon, 69 | color: 'text-danger-50', 70 | requiresAuth: true, 71 | }, 72 | }, 73 | { 74 | path: 'tags', 75 | name: 'Tags', 76 | component: Tags, 77 | meta: { 78 | title: 'Tags', 79 | icon: CursorClickIcon, 80 | color: 'text-primary', 81 | requiresAuth: true, 82 | }, 83 | }, 84 | { 85 | path: 'notifications', 86 | component: Notification, 87 | name: 'Notifications', 88 | meta: { 89 | title: 'Notifications', 90 | icon: BellIcon, 91 | color: 'text-success-50', 92 | requiresAuth: true, 93 | }, 94 | }, 95 | { 96 | path: 'tables', 97 | component: Table, 98 | name: 'Tables', 99 | meta: { 100 | title: 'Tables', 101 | icon: ViewBoardsIcon, 102 | color: 'text-indigo-410', 103 | requiresAuth: true, 104 | }, 105 | }, 106 | { 107 | path: 'grid', 108 | component: Grid, 109 | name: 'Grid', 110 | meta: { 111 | title: 'Grid', 112 | icon: ViewGridIcon, 113 | color: 'text-info', 114 | requiresAuth: true, 115 | }, 116 | }, 117 | { 118 | path: 'typography', 119 | component: Typography, 120 | name: 'Typography', 121 | meta: { 122 | title: 'Typography', 123 | icon: DocumentTextIcon, 124 | color: 'text-yellow-310', 125 | requiresAuth: true, 126 | }, 127 | }, 128 | { 129 | path: 'cards', 130 | component: Card, 131 | name: 'Cards', 132 | meta: { 133 | title: 'Cards', 134 | icon: CreditCardIcon, 135 | color: 'text-warning-50', 136 | requiresAuth: true, 137 | }, 138 | }, 139 | { 140 | path: 'icons', 141 | component: Icons, 142 | name: 'Icons', 143 | meta: { 144 | title: 'Icons', 145 | icon: StarIcon, 146 | color: 'text-red-410', 147 | requiresAuth: true, 148 | }, 149 | }, 150 | ] 151 | }, 152 | 153 | { 154 | path: '/profile', 155 | component: Profile, 156 | name: 'Profile', 157 | meta: { 158 | title: 'Profile', 159 | icon: UserIcon, 160 | color: 'text-success-50', 161 | isDarkBackground: true, 162 | isFullWidthLayout: true, 163 | requiresAuth: true, 164 | parentPath: 'Home' 165 | }, 166 | }, 167 | { 168 | path: '/map', 169 | component: Map, 170 | name: 'Map', 171 | meta: { 172 | title: 'Map', 173 | icon: LocationMarkerIcon, 174 | color: 'text-red-410', 175 | requiresAuth: true, 176 | parentPath: 'Home' 177 | }, 178 | }, 179 | { 180 | path: '/register', 181 | component: Register, 182 | name: 'register', 183 | meta: { 184 | requiresAuth: false, 185 | }, 186 | }, 187 | { 188 | path: '/forgot-password', 189 | component: ForgotPassword, 190 | name: 'forgot-password', 191 | meta: { 192 | requiresAuth: false, 193 | }, 194 | }, 195 | { 196 | 197 | path: '/login', 198 | component: Login, 199 | name: 'login', 200 | meta: { 201 | requiresAuth: false, 202 | }, 203 | }, 204 | { 205 | path: '/:pathMatch(.*)*', 206 | component: NotFound, 207 | name: 'NotFound', 208 | meta: { 209 | requiresAuth: false, 210 | }, 211 | }, 212 | ] 213 | 214 | export default routes 215 | -------------------------------------------------------------------------------- /src/shims-element-plugins.d.ts: -------------------------------------------------------------------------------- 1 | import '@vue/runtime-core' 2 | import { IElMessageBox, Message } from 'element-plus' 3 | 4 | declare module '@vue/runtime-core' { 5 | interface ComponentCustomProperties { 6 | $message: Message 7 | $messageBox: IElMessageBox 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/shims-element-plus.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'element-plus/*' 2 | -------------------------------------------------------------------------------- /src/shims-global.d.ts: -------------------------------------------------------------------------------- 1 | // declare module 2 | declare module 'vue-sweetalert2' 3 | declare module 'vue-chart-3' 4 | declare module '@fortawesome/fontawesome-svg-core' 5 | declare module '@fortawesome/free-solid-svg-icons' 6 | declare module '@fortawesome/free-brands-svg-icons' 7 | -------------------------------------------------------------------------------- /src/shims-router.d.ts: -------------------------------------------------------------------------------- 1 | import 'vue-router' 2 | 3 | declare module 'vue-router' { 4 | interface RouteMeta { 5 | requiresAuth?: boolean 6 | permissions?: string[] 7 | title?: string 8 | show2ndSidebar?: boolean 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { useDashboardStore } from 'modules/dashboard/store'; 2 | import { useAuthStore } from 'modules/auth/store'; 3 | import { defineStore } from 'pinia' 4 | 5 | export const globalStore = defineStore('global', { 6 | state: () => ({ 7 | loading: false, 8 | }), 9 | actions: { 10 | async actLoading(status: boolean) { 11 | this.loading = status 12 | }, 13 | }, 14 | }) 15 | 16 | const useStore = () => ({ 17 | dashboard: useDashboardStore(), 18 | auth: useAuthStore(), 19 | global: globalStore(), 20 | }); 21 | 22 | export default useStore; -------------------------------------------------------------------------------- /src/utils/extractStore.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | PiniaCustomStateProperties, 3 | StoreActions, 4 | StoreGeneric, 5 | StoreGetters, 6 | StoreState, 7 | } from 'pinia' 8 | import type { ToRefs } from 'vue' 9 | import { isReactive, isRef, toRaw, toRef } from 'vue' 10 | 11 | type Extracted = ToRefs & StoreGetters & PiniaCustomStateProperties> & 12 | StoreActions 13 | 14 | /** 15 | * Creates an object of references with all the state, getters, actions 16 | * and plugin-added state properties of the store. 17 | * 18 | * @param store - store to extract the refs from 19 | */ 20 | export function extractStore(store: SS): Extracted { 21 | const rawStore = toRaw(store) 22 | const refs: Record = {} 23 | 24 | for (const [key, value] of Object.entries(rawStore)) { 25 | if (isRef(value) || isReactive(value)) { 26 | refs[key] = toRef(store, key) 27 | } else if (typeof value === 'function') { 28 | refs[key] = value 29 | } 30 | } 31 | 32 | // eslint-disable-next-line @typescript-eslint/consistent-type-assertions 33 | return refs as Extracted 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs' 2 | export const formatDate = (date: Date, format: string = 'hh:mm') => { 3 | return dayjs(date).format(format) 4 | } 5 | 6 | export const checkIsMobile = () => { 7 | return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) 8 | } 9 | -------------------------------------------------------------------------------- /tools/store/index: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const path = require('path') 3 | const fs = require('fs') 4 | const yargs = require('yargs') 5 | const { hideBin } = require('yargs/helpers') 6 | 7 | const projectDir = path.resolve(__dirname, '..', '..') 8 | const srcDir = path.resolve(projectDir, 'src') 9 | const templateDir = path.resolve(__dirname, 'templates') 10 | 11 | const log = { 12 | debug: console.log, 13 | info: console.log, 14 | warn: console.log, 15 | error: console.error, 16 | } 17 | 18 | const readTplFile = (filename) => { 19 | return fs.readFileSync(path.resolve(templateDir, `${filename}.ts.tpl`), { 20 | encoding: 'utf-8', 21 | }) 22 | } 23 | 24 | const writeFile = (filename, data) => { 25 | return fs.writeFileSync(filename, data, { encoding: 'utf-8' }) 26 | } 27 | 28 | function capitalize(string) { 29 | return string.charAt(0).toUpperCase() + string.slice(1) 30 | } 31 | 32 | const parseTemplate = (template = '', data = {}) => { 33 | const keys = Object.keys(data) 34 | return keys.reduce( 35 | (parsed, key) => parsed.replace(new RegExp(`{{${key}}}`, 'g'), data[key]), 36 | template, 37 | ) 38 | } 39 | 40 | const create = { 41 | command: 'create ', 42 | describe: 'Create module store', 43 | // builder(yargs) { 44 | // // yargs.options('') 45 | // }, 46 | handler(opts) { 47 | const moduleName = opts.moduleName.toLowerCase() 48 | const moduleDir = path.resolve(srcDir, 'modules', moduleName) 49 | const isExisted = fs.existsSync(moduleDir) 50 | if (!isExisted) { 51 | log.error(`The module '${moduleName}' does not exist at ${moduleDir}`) 52 | return 53 | } 54 | const tplNames = [ 55 | 'index', 56 | 'types', 57 | 'state', 58 | 'actions', 59 | 'getters', 60 | 'mutations', 61 | ] 62 | tplNames.forEach((name) => { 63 | const tpl = readTplFile(name) 64 | const data = { moduleName, typeName: capitalize(moduleName) } 65 | const parsed = parseTemplate(tpl, data) 66 | writeFile(path.resolve(moduleDir, 'store', `${name}.ts`), parsed) 67 | }) 68 | }, 69 | } 70 | 71 | yargs(hideBin(process.argv)) 72 | .usage('Usage: $0 [options]') 73 | .version() 74 | .command(create) 75 | .help().argv 76 | -------------------------------------------------------------------------------- /tools/store/templates/actions.ts.tpl: -------------------------------------------------------------------------------- 1 | import { RootState } from 'store' 2 | import { ActionTree } from 'vuex' 3 | import { {{typeName}}State } from './types' 4 | 5 | export const actions: ActionTree<{{typeName}}State, RootState> = {} 6 | -------------------------------------------------------------------------------- /tools/store/templates/getters.ts.tpl: -------------------------------------------------------------------------------- 1 | import { GetterTree } from 'vuex' 2 | import { RootState } from 'store' 3 | import { {{typeName}}State } from './types' 4 | 5 | export const getters: GetterTree<{{typeName}}State, RootState> = {} 6 | -------------------------------------------------------------------------------- /tools/store/templates/index.ts.tpl: -------------------------------------------------------------------------------- 1 | import { Module } from 'vuex' 2 | import { state } from './state' 3 | import { actions } from './actions' 4 | import { getters } from './getters' 5 | import { mutations } from './mutations' 6 | import { RootState } from 'store' 7 | import { {{typeName}}State } from './types' 8 | 9 | export const {{moduleName}}: Module<{{typeName}}State, RootState> = { 10 | namespaced: true, 11 | state, 12 | getters, 13 | actions, 14 | mutations, 15 | } 16 | -------------------------------------------------------------------------------- /tools/store/templates/mutations.ts.tpl: -------------------------------------------------------------------------------- 1 | import { MutationTree } from 'vuex' 2 | import { {{typeName}}State } from './types' 3 | 4 | export const mutations: MutationTree<{{typeName}}State> = {} 5 | -------------------------------------------------------------------------------- /tools/store/templates/state.ts.tpl: -------------------------------------------------------------------------------- 1 | import { {{typeName}}State } from './types' 2 | 3 | export const state: {{typeName}}State = {} 4 | -------------------------------------------------------------------------------- /tools/store/templates/types.ts.tpl: -------------------------------------------------------------------------------- 1 | export interface {{typeName}}State {} 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "importHelpers": true, 13 | "allowSyntheticDefaultImports": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "lib": ["esnext", "dom"], 16 | "types": ["vite/client", "node", "vue-sweetalert2"], 17 | "typeRoots": ["../../../node_modules/@types", "node_modules/@types"], 18 | "baseUrl": ".", 19 | "paths": { 20 | "core/*": ["src/core/*"], 21 | "components": ["src/components/index.ts"], 22 | "components/*": ["src/components/*"], 23 | "layouts/*": ["src/layouts/*"], 24 | "store/*": ["src/store/*"], 25 | "store": ["src/store/index"], 26 | "modules/*": ["src/modules/*"], 27 | "router/*": ["src/router/*"], 28 | "router": ["src/router/index"], 29 | "utils/*": ["src/utils/*"], 30 | "mixins": ["src/mixins/index.ts"], 31 | } 32 | }, 33 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 34 | "exclude": ["node_modules", "node_modules/**"] 35 | } 36 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue' 2 | import path from 'path' 3 | import { defineConfig } from 'vite' 4 | const { visualizer } = require('rollup-plugin-visualizer') 5 | import AutoImport from 'unplugin-auto-import/vite' 6 | import Components from 'unplugin-vue-components/vite' 7 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 8 | 9 | const resolvePath = (dir: string) => { 10 | return path.resolve(__dirname, 'src', dir) 11 | } 12 | 13 | export default defineConfig({ 14 | plugins: [ 15 | vue(), 16 | AutoImport({ 17 | resolvers: [ElementPlusResolver()], 18 | }), 19 | Components({ 20 | resolvers: [ElementPlusResolver({ importStyle: 'sass' })], 21 | }), 22 | ], 23 | resolve: { 24 | alias: { 25 | assets: resolvePath('assets'), 26 | core: resolvePath('core'), 27 | store: resolvePath('store'), 28 | modules: resolvePath('modules'), 29 | layouts: resolvePath('layouts'), 30 | router: resolvePath('router'), 31 | components: resolvePath('components'), 32 | utils: resolvePath('utils'), 33 | mixins: resolvePath('mixins'), 34 | '@': path.resolve(__dirname, './src'), 35 | }, 36 | }, 37 | css: { 38 | preprocessorOptions: { 39 | scss: { 40 | additionalData: `@use "assets/css/element/index.scss" as *;`, 41 | }, 42 | }, 43 | }, 44 | build: { 45 | rollupOptions: { 46 | plugins: [visualizer()], 47 | output: { 48 | manualChunks(id) { 49 | if (id.includes('element-plus')) { 50 | return 'elm' 51 | } 52 | if (id.includes('lodash')) { 53 | return 'lodash' 54 | } 55 | if (id.includes('element-plus')) { 56 | return 'elm' 57 | } 58 | if (id.includes('@sentry')) { 59 | return 'sentry' 60 | } 61 | if (id.includes('node_modules')) { 62 | return 'vendor' 63 | } 64 | }, 65 | }, 66 | }, 67 | sourcemap: true, 68 | }, 69 | optimizeDeps: {}, 70 | }) 71 | --------------------------------------------------------------------------------