├── .gitignore ├── README.md ├── client ├── .github │ └── workflows │ │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── Documentation │ └── documentation.html ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── gulpfile.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public │ ├── apple-icon.png │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.js │ ├── assets │ ├── css │ │ ├── argon-dashboard-react.css │ │ ├── argon-dashboard-react.css.map │ │ └── argon-dashboard-react.min.css │ ├── fonts │ │ ├── nucleo.eot │ │ ├── nucleo.ttf │ │ ├── nucleo.woff │ │ └── nucleo.woff2 │ ├── img │ │ ├── brand │ │ │ ├── argon-react-white.png │ │ │ ├── argon-react.png │ │ │ ├── blue.png │ │ │ ├── bug-tracker-logo.png │ │ │ ├── favicon.png │ │ │ └── white.png │ │ ├── icons │ │ │ └── common │ │ │ │ ├── github.svg │ │ │ │ └── google.svg │ │ └── theme │ │ │ ├── angular.jpg │ │ │ ├── bootstrap.jpg │ │ │ ├── profile-cover.jpg │ │ │ ├── react.jpg │ │ │ ├── sketch.jpg │ │ │ ├── team-1-800x800.jpg │ │ │ ├── team-2-800x800.jpg │ │ │ ├── team-3-800x800.jpg │ │ │ ├── team-4-800x800.jpg │ │ │ └── vue.jpg │ ├── plugins │ │ └── nucleo │ │ │ ├── css │ │ │ ├── nucleo-svg.css │ │ │ └── nucleo.css │ │ │ └── fonts │ │ │ ├── nucleo-icons.eot │ │ │ ├── nucleo-icons.svg │ │ │ ├── nucleo-icons.ttf │ │ │ ├── nucleo-icons.woff │ │ │ └── nucleo-icons.woff2 │ └── scss │ │ ├── argon-dashboard-react.scss │ │ ├── argon-dashboard │ │ ├── custom │ │ │ ├── _alert.scss │ │ │ ├── _avatar.scss │ │ │ ├── _badge.scss │ │ │ ├── _buttons.scss │ │ │ ├── _card.scss │ │ │ ├── _chart.scss │ │ │ ├── _close.scss │ │ │ ├── _components.scss │ │ │ ├── _content.scss │ │ │ ├── _custom-forms.scss │ │ │ ├── _dropdown.scss │ │ │ ├── _footer.scss │ │ │ ├── _forms.scss │ │ │ ├── _functions.scss │ │ │ ├── _header.scss │ │ │ ├── _icons.scss │ │ │ ├── _input-group.scss │ │ │ ├── _list-group.scss │ │ │ ├── _map.scss │ │ │ ├── _mask.scss │ │ │ ├── _mixins.scss │ │ │ ├── _modal.scss │ │ │ ├── _nav.scss │ │ │ ├── _navbar.scss │ │ │ ├── _pagination.scss │ │ │ ├── _popover.scss │ │ │ ├── _progress.scss │ │ │ ├── _reboot.scss │ │ │ ├── _section.scss │ │ │ ├── _separator.scss │ │ │ ├── _tables.scss │ │ │ ├── _type.scss │ │ │ ├── _utilities.scss │ │ │ ├── _variables.scss │ │ │ ├── _vendors.scss │ │ │ ├── alerts │ │ │ │ ├── _alert-dismissible.scss │ │ │ │ └── _alert.scss │ │ │ ├── avatars │ │ │ │ ├── _avatar-group.scss │ │ │ │ └── _avatar.scss │ │ │ ├── badges │ │ │ │ ├── _badge-circle.scss │ │ │ │ ├── _badge-dot.scss │ │ │ │ └── _badge.scss │ │ │ ├── buttons │ │ │ │ ├── _button-brand.scss │ │ │ │ ├── _button-icon.scss │ │ │ │ └── _button.scss │ │ │ ├── cards │ │ │ │ ├── _card-animations.scss │ │ │ │ ├── _card-blockquote.scss │ │ │ │ ├── _card-profile.scss │ │ │ │ ├── _card-stats.scss │ │ │ │ └── _card.scss │ │ │ ├── charts │ │ │ │ └── _chart.scss │ │ │ ├── close │ │ │ │ └── _close.scss │ │ │ ├── custom-forms │ │ │ │ ├── _custom-checkbox.scss │ │ │ │ ├── _custom-control.scss │ │ │ │ ├── _custom-form.scss │ │ │ │ ├── _custom-radio.scss │ │ │ │ └── _custom-toggle.scss │ │ │ ├── dropdowns │ │ │ │ └── _dropdown.scss │ │ │ ├── footers │ │ │ │ └── _footer.scss │ │ │ ├── forms │ │ │ │ ├── _form-validation.scss │ │ │ │ ├── _form.scss │ │ │ │ └── _input-group.scss │ │ │ ├── headers │ │ │ │ └── _header.scss │ │ │ ├── icons │ │ │ │ ├── _icon-shape.scss │ │ │ │ └── _icon.scss │ │ │ ├── list-groups │ │ │ │ └── _list-group.scss │ │ │ ├── maps │ │ │ │ └── _map.scss │ │ │ ├── masks │ │ │ │ └── _mask.scss │ │ │ ├── mixins │ │ │ │ ├── _alert.scss │ │ │ │ ├── _background-variant.scss │ │ │ │ ├── _badge.scss │ │ │ │ ├── _buttons.scss │ │ │ │ ├── _forms.scss │ │ │ │ ├── _icon.scss │ │ │ │ ├── _modals.scss │ │ │ │ └── _popover.scss │ │ │ ├── modals │ │ │ │ └── _modal.scss │ │ │ ├── navbars │ │ │ │ ├── _navbar-collapse.scss │ │ │ │ ├── _navbar-dropdown.scss │ │ │ │ ├── _navbar-search.scss │ │ │ │ ├── _navbar-vertical.scss │ │ │ │ └── _navbar.scss │ │ │ ├── navs │ │ │ │ ├── _nav-pills.scss │ │ │ │ └── _nav.scss │ │ │ ├── paginations │ │ │ │ └── _pagination.scss │ │ │ ├── popovers │ │ │ │ └── _popover.scss │ │ │ ├── progresses │ │ │ │ └── _progress.scss │ │ │ ├── separators │ │ │ │ └── _separator.scss │ │ │ ├── tables │ │ │ │ └── _table.scss │ │ │ ├── type │ │ │ │ ├── _article.scss │ │ │ │ ├── _display.scss │ │ │ │ ├── _heading.scss │ │ │ │ └── _type.scss │ │ │ ├── utilities │ │ │ │ ├── _backgrounds.scss │ │ │ │ ├── _blurable.scss │ │ │ │ ├── _floating.scss │ │ │ │ ├── _helper.scss │ │ │ │ ├── _image.scss │ │ │ │ ├── _opacity.scss │ │ │ │ ├── _overflow.scss │ │ │ │ ├── _position.scss │ │ │ │ ├── _shadows.scss │ │ │ │ ├── _sizing.scss │ │ │ │ ├── _spacing.scss │ │ │ │ ├── _text.scss │ │ │ │ └── _transform.scss │ │ │ └── vendors │ │ │ │ ├── _bootstrap-datepicker.scss │ │ │ │ ├── _headroom.scss │ │ │ │ ├── _nouislider.scss │ │ │ │ └── _scrollbar.scss │ │ └── docs │ │ │ ├── _clipboard-js.scss │ │ │ ├── _component-examples.scss │ │ │ ├── _content.scss │ │ │ ├── _footer.scss │ │ │ ├── _nav.scss │ │ │ ├── _prism.scss │ │ │ ├── _sidebar.scss │ │ │ └── _variables.scss │ │ └── react │ │ ├── _buttons.scss │ │ ├── _mixins.scss │ │ ├── _navbar-dropdown.scss │ │ ├── _navbar.scss │ │ ├── _tables.scss │ │ ├── bootstrap │ │ └── _spinners.scss │ │ ├── plugins │ │ └── _plugin-react-datetime.scss │ │ └── react-differences.scss │ ├── components │ ├── Charts │ │ └── TicketsPieChart.js │ ├── Footers │ │ ├── AdminFooter.js │ │ └── AuthFooter.js │ ├── Forms │ │ ├── AddTeamMember.js │ │ ├── CreateProject.js │ │ ├── CreateTicket.js │ │ ├── UpdateProject.js │ │ ├── UpdateTicket.js │ │ └── useForm.js │ ├── Headers │ │ ├── Header.js │ │ └── UserHeader.js │ ├── Modal │ │ └── Modal.js │ ├── Navbars │ │ ├── AdminNavbar.js │ │ └── AuthNavbar.js │ ├── Sidebar │ │ ├── GeneralSidebar.js │ │ └── UniversalSidebar.js │ ├── Tables │ │ ├── DataTable.js │ │ ├── PaginationComponent.js │ │ ├── ProjectTeamTable.js │ │ ├── ProjectTicketsTable.js │ │ ├── ProjectsTable.js │ │ └── UsersCell.js │ └── Tickets │ │ └── SelectedTicket.js │ ├── contexts │ └── AuthContext.js │ ├── index.css │ ├── index.js │ ├── layouts │ ├── Auth.js │ ├── General.js │ └── Main.js │ ├── reportWebVitals.js │ ├── routes.js │ ├── setupTests.js │ ├── utils │ ├── API.js │ └── formValidation │ │ ├── loginValidation.js │ │ ├── registerValidation.js │ │ └── ticketValidation.js │ ├── variables │ └── charts.js │ └── views │ ├── Administration.js │ ├── Index.js │ ├── Login.js │ ├── Project.js │ ├── Register.js │ ├── Tables.css │ └── Tickets.js ├── controllers ├── availableUsersController.js ├── commentController.js ├── devAssignmentsController.js ├── projectController.js ├── ticketController.js ├── userController.js └── userProjectController.js ├── db.js ├── middleware └── authorization.js ├── package-lock.json ├── package.json ├── routes ├── api │ ├── auth.js │ ├── availableUsers.js │ ├── comment.js │ ├── devAssignments.js │ ├── index.js │ ├── login.js │ ├── project.js │ ├── ticket.js │ ├── user.js │ └── userProjects.js └── index.js ├── server.js └── utils └── jwtGenerator.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | .env 21 | 22 | /schema 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | requests.rest -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BugTracker 2 | 3 | *Project Management - Clean and Responsive* 4 | 5 | ## Purpose 6 | Bug Tracker is a project management tool to track progress of multiple projects and teams. Users are able to create projects and assign developers to the project. Within a project, users can generate tickets for bugs, feature requests, etc and follow comment streams on individual tickets. 7 | 8 | ## Usage 9 | Log into BugTracker after creating an account and begin by adding a new project from the dashboard. 10 | 11 | ## Technologies Used 12 | - React 13 | - Node.js 14 | - PostgreSQL 15 | - Authorization / Authentication utilizing JSON Web Tokens 16 | - Deployed through Heroku 17 | 18 | ## Development Notes 19 | ### Prerequisites 20 | **Node** 21 | 22 | Before you can install Node, you’ll need to install two other applications. Fortunately, once you have these on your machine, installing Node takes just a few minutes.[1] 23 | 24 | **Mac OS** 25 | > - **XCode** Apple’s XCode development software is used to build Mac and iOS apps, but it also includes the tools you need to compile software for use on your Mac. XCode is free and you can find it in the Apple App Store. 26 | > 27 | > - Via Terminal `xcode-select --install` 28 | > 29 | > - **Homebrew** Homebrew is a package manager for the Mac — it makes installing most open source sofware (like Node) as simple as writing `brew install node`. 30 | > - To install Homebrew just open Terminal and type `ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`. You’ll see messages in the Terminal explaining what you need to do to complete the installation process. Now type `brew install node`. 31 | 32 | **Windows Installation Steps**[2] 33 | > - Download the Windows installer from the [Nodes.js®](http://nodejs.org/) web site. 34 | > - Run the installer (the .msi file you downloaded in the previous step.) 35 | > - Follow the prompts in the installer (Accept the license agreement, click the NEXT button a bunch of times and accept the default installation settings). 36 | > - Restart your computer. You won’t be able to run Node.js until you restart your computer. 37 | 38 | ## Contributing 39 | - Fork it (https://github.com/connorleee/BugTracker/fork) 40 | - Create your feature branch `git checkout -b feature/newFeature` 41 | - Commit your changes `git commit -am 'Add your Message Here'` 42 | - Push to the branch `git push origin feature/newFeature` 43 | - Create a new Pull Request within Github 44 | 45 | ## Support 46 | - Open a new issue [here](https://github.com/connorleee/BugTracker/issues/) for support. 47 | 48 | ## Team 49 | - **Connor Lee** - https://connorleee.github.io/portfolio-official/ 50 | 51 | 52 | ## Acknowledgments 53 | 54 | [1] Adapted from instructions found here: https://blog.teamtreehouse.com/install-node-js-npm-mac 55 | 56 | [2] Adapted from instructions found here: https://blog.teamtreehouse.com/install-node-js-npm-windows 57 | 58 | [3] Utilizes Front End Template by Creative-Tim: https://www.creative-tim.com/product/argon-dashboard-react 59 | -------------------------------------------------------------------------------- /client/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Autocloser 2 | on: [issues] 3 | jobs: 4 | autoclose: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Issue auto-closer 8 | uses: roots/issue-closer-action@v1.1 9 | with: 10 | repo-token: ${{ secrets.GITHUB_TOKEN }} 11 | issue-close-message: "@${issue.user.login} this issue was automatically closed because it did not follow our rules:\n\n
\n\n\n\nIMPORTANT: Please use the following link to create a new issue:\n\nhttps://www.creative-tim.com/new-issue/argon-dashboard-react\n\n**If your issue was not created using the app above, it will be closed immediately.**\n\n\n\nLove Creative Tim? Do you need Angular, React, Vuejs or HTML? You can visit:\n👉  https://www.creative-tim.com/bundles\n👉  https://www.creative-tim.com\n\n\n
\n\n" 12 | issue-pattern: (\#\#\# Version([\S\s.*]*?)\#\#\# Reproduction link([\S\s.*]*?)\#\#\# Operating System([\S\s.*]*?)\#\#\# Device([\S\s.*]*?)\#\#\# Browser & Version([\S\s.*]*?)\#\#\# Steps to reproduce([\S\s.*]*?)\#\#\# What is expected([\S\s.*]*?)\#\#\# What is actually happening([\S\s.*]*?)---([\S\s.*]*?)\#\#\# Solution([\S\s.*]*?)\#\#\# Additional comments([\S\s.*]*?)\<\!-- generated by creative-tim-issues\. DO NOT REMOVE --\>)|(\#\#\# What is your enhancement([\S\s.*]*?)\<\!-- generated by creative-tim-issues\. DO NOT REMOVE --\>) 13 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /node_modules 3 | package-lock.json 4 | /build 5 | .eslintcache 6 | -------------------------------------------------------------------------------- /client/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /client/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Creative Tim 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 | -------------------------------------------------------------------------------- /client/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require("gulp"); 2 | const gap = require("gulp-append-prepend"); 3 | 4 | gulp.task("licenses", async function () { 5 | // this is to add Creative Tim licenses in the production mode for the minified js 6 | gulp 7 | .src("build/static/js/*chunk.js", { base: "./" }) 8 | .pipe( 9 | gap.prependText(`/*! 10 | 11 | ========================================================= 12 | * Argon Dashboard React - v1.2.0 13 | ========================================================= 14 | 15 | * Product Page: https://www.creative-tim.com/product/argon-dashboard-react 16 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 17 | * Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md) 18 | 19 | * Coded by Creative Tim 20 | 21 | ========================================================= 22 | 23 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 24 | 25 | */`) 26 | ) 27 | .pipe(gulp.dest("./", { overwrite: true })); 28 | 29 | // this is to add Creative Tim licenses in the production mode for the minified html 30 | gulp 31 | .src("build/index.html", { base: "./" }) 32 | .pipe( 33 | gap.prependText(``) 50 | ) 51 | .pipe(gulp.dest("./", { overwrite: true })); 52 | 53 | // this is to add Creative Tim licenses in the production mode for the minified css 54 | gulp 55 | .src("build/static/css/*chunk.css", { base: "./" }) 56 | .pipe( 57 | gap.prependText(`/*! 58 | 59 | ========================================================= 60 | * Argon Dashboard React - v1.2.0 61 | ========================================================= 62 | 63 | * Product Page: https://www.creative-tim.com/product/argon-dashboard-react 64 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 65 | * Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md) 66 | 67 | * Coded by Creative Tim 68 | 69 | ========================================================= 70 | 71 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 72 | 73 | */`) 74 | ) 75 | .pipe(gulp.dest("./", { overwrite: true })); 76 | return; 77 | }); 78 | -------------------------------------------------------------------------------- /client/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "paths": { 5 | "*": ["src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bug-tracker-client", 3 | "version": "1.2.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [ 7 | "react", 8 | "reactjs", 9 | "argon", 10 | "argon-react", 11 | "dashboard", 12 | "dashboard-react", 13 | "argon-dashboard", 14 | "argon-dashboard-react" 15 | ], 16 | "author": "Connor Lee", 17 | "license": "MIT", 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject", 23 | "install:clean": "rm -rf node_modules/ && rm -rf package-lock.json && npm install && npm start", 24 | "compile:scss": "node-sass --importer node_modules/node-sass-package-importer/dist/cli.js src/assets/scss/argon-dashboard-react.scss src/assets/css/argon-dashboard-react.css", 25 | "minify:scss": "node-sass --importer node_modules/node-sass-package-importer/dist/cli.js src/assets/scss/argon-dashboard-react.scss src/assets/css/argon-dashboard-react.min.css --output-style compressed", 26 | "map:scss": "node-sass --importer node_modules/node-sass-package-importer/dist/cli.js src/assets/scss/argon-dashboard-react.scss src/assets/css/argon-dashboard-react.css --source-map true", 27 | "build:scss": "npm run compile:scss && npm run minify:scss && npm run map:scss" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": [ 33 | ">0.2%", 34 | "not dead", 35 | "not ie <= 11", 36 | "not op_mini all" 37 | ], 38 | "dependencies": { 39 | "@fortawesome/fontawesome-free": "5.15.2", 40 | "axios": "^0.21.1", 41 | "bootstrap": "4.6.0", 42 | "chart.js": "2.9.4", 43 | "classnames": "2.2.6", 44 | "moment": "^2.29.1", 45 | "node-sass": "4.14.1", 46 | "node-sass-package-importer": "5.3.2", 47 | "nouislider": "14.6.3", 48 | "react": "17.0.1", 49 | "react-chartjs-2": "2.11.1", 50 | "react-copy-to-clipboard": "5.0.3", 51 | "react-datetime": "3.0.4", 52 | "react-dom": "17.0.1", 53 | "react-router-dom": "5.2.0", 54 | "react-scripts": "4.0.1", 55 | "reactstrap": "8.9.0" 56 | }, 57 | "devDependencies": { 58 | "gulp": "4.0.2", 59 | "gulp-append-prepend": "1.0.8" 60 | }, 61 | "optionalDependencies": { 62 | "eslint-plugin-flowtype": "5.2.0", 63 | "jquery": "3.5.1", 64 | "typescript": "4.1.3" 65 | }, 66 | "proxy": "http://localhost:3001" 67 | } 68 | -------------------------------------------------------------------------------- /client/public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/public/apple-icon.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 21 | 22 | 26 | Bug Tracker 27 | 28 | 29 | 30 |
31 |
32 | 42 | 43 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "BLK Design System React", 3 | "name": "BLK Design System React by Creative Tim", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom"; 3 | import MainLayout from "layouts/Main.js"; 4 | import AuthLayout from "layouts/Auth.js"; 5 | 6 | const App = () => { 7 | const [isAuthenticated, setIsAuthenticated] = useState(true); 8 | const [authLevel, setAuthLevel] = useState(""); 9 | 10 | let token = localStorage.getItem("token"); 11 | 12 | useEffect(() => { 13 | if (token == null) { 14 | setIsAuthenticated(false); 15 | } 16 | }, [token]); 17 | 18 | const setAuth = (boolean) => { 19 | setIsAuthenticated(boolean); 20 | }; 21 | 22 | return ( 23 | 24 | 25 | { 28 | if (!isAuthenticated) { 29 | return ( 30 | 35 | ); 36 | } 37 | }} 38 | /> 39 | 40 | 43 | isAuthenticated && token !== null ? ( 44 | 50 | ) : ( 51 | 52 | ) 53 | } 54 | /> 55 | 56 | 57 |

404 No page found

58 |
59 |
60 |
61 | ); 62 | }; 63 | 64 | export default App; 65 | -------------------------------------------------------------------------------- /client/src/assets/fonts/nucleo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/fonts/nucleo.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/nucleo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/fonts/nucleo.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/nucleo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/fonts/nucleo.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/nucleo.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/fonts/nucleo.woff2 -------------------------------------------------------------------------------- /client/src/assets/img/brand/argon-react-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/argon-react-white.png -------------------------------------------------------------------------------- /client/src/assets/img/brand/argon-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/argon-react.png -------------------------------------------------------------------------------- /client/src/assets/img/brand/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/blue.png -------------------------------------------------------------------------------- /client/src/assets/img/brand/bug-tracker-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/bug-tracker-logo.png -------------------------------------------------------------------------------- /client/src/assets/img/brand/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/favicon.png -------------------------------------------------------------------------------- /client/src/assets/img/brand/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/brand/white.png -------------------------------------------------------------------------------- /client/src/assets/img/icons/common/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UI/icons/dark/github 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /client/src/assets/img/icons/common/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UI/icons/color/google 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /client/src/assets/img/theme/angular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/angular.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/bootstrap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/bootstrap.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/profile-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/profile-cover.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/react.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/sketch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/sketch.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/team-1-800x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/team-1-800x800.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/team-2-800x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/team-2-800x800.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/team-3-800x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/team-3-800x800.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/team-4-800x800.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/team-4-800x800.jpg -------------------------------------------------------------------------------- /client/src/assets/img/theme/vue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/img/theme/vue.jpg -------------------------------------------------------------------------------- /client/src/assets/plugins/nucleo/css/nucleo-svg.css: -------------------------------------------------------------------------------- 1 | /* Generated using nucleoapp.com */ 2 | /* -------------------------------- 3 | 4 | Icon colors 5 | 6 | -------------------------------- */ 7 | 8 | .icon { 9 | display: inline-block; 10 | /* icon primary color */ 11 | color: #111111; 12 | height: 1em; 13 | width: 1em; 14 | } 15 | 16 | .icon use { 17 | /* icon secondary color - fill */ 18 | fill: #7ea6f6; 19 | } 20 | 21 | .icon.icon-outline use { 22 | /* icon secondary color - stroke */ 23 | stroke: #7ea6f6; 24 | } 25 | 26 | /* -------------------------------- 27 | 28 | Change icon size 29 | 30 | -------------------------------- */ 31 | 32 | .icon-xs { 33 | height: 0.5em; 34 | width: 0.5em; 35 | } 36 | 37 | .icon-sm { 38 | height: 0.8em; 39 | width: 0.8em; 40 | } 41 | 42 | .icon-lg { 43 | height: 1.6em; 44 | width: 1.6em; 45 | } 46 | 47 | .icon-xl { 48 | height: 2em; 49 | width: 2em; 50 | } 51 | 52 | /* -------------------------------- 53 | 54 | Align icon and text 55 | 56 | -------------------------------- */ 57 | 58 | .icon-text-aligner { 59 | /* add this class to parent element that contains icon + text */ 60 | display: flex; 61 | align-items: center; 62 | } 63 | 64 | .icon-text-aligner .icon { 65 | color: inherit; 66 | margin-right: 0.4em; 67 | } 68 | 69 | .icon-text-aligner .icon use { 70 | color: inherit; 71 | fill: currentColor; 72 | } 73 | 74 | .icon-text-aligner .icon.icon-outline use { 75 | stroke: currentColor; 76 | } 77 | 78 | /* -------------------------------- 79 | 80 | Icon reset values - used to enable color customizations 81 | 82 | -------------------------------- */ 83 | 84 | .icon { 85 | fill: currentColor; 86 | stroke: none; 87 | } 88 | 89 | .icon.icon-outline { 90 | fill: none; 91 | stroke: currentColor; 92 | } 93 | 94 | .icon use { 95 | stroke: none; 96 | } 97 | 98 | .icon.icon-outline use { 99 | fill: none; 100 | } 101 | 102 | /* -------------------------------- 103 | 104 | Stroke effects - Nucleo outline icons 105 | 106 | - 16px icons -> up to 1px stroke (16px outline icons do not support stroke changes) 107 | - 24px, 32px icons -> up to 2px stroke 108 | - 48px, 64px icons -> up to 4px stroke 109 | 110 | -------------------------------- */ 111 | 112 | .icon-outline.icon-stroke-1 { 113 | stroke-width: 1px; 114 | } 115 | 116 | .icon-outline.icon-stroke-2 { 117 | stroke-width: 2px; 118 | } 119 | 120 | .icon-outline.icon-stroke-3 { 121 | stroke-width: 3px; 122 | } 123 | 124 | .icon-outline.icon-stroke-4 { 125 | stroke-width: 4px; 126 | } 127 | 128 | .icon-outline.icon-stroke-1 use, 129 | .icon-outline.icon-stroke-3 use { 130 | -webkit-transform: translateX(0.5px) translateY(0.5px); 131 | -moz-transform: translateX(0.5px) translateY(0.5px); 132 | -ms-transform: translateX(0.5px) translateY(0.5px); 133 | -o-transform: translateX(0.5px) translateY(0.5px); 134 | transform: translateX(0.5px) translateY(0.5px); 135 | } -------------------------------------------------------------------------------- /client/src/assets/plugins/nucleo/fonts/nucleo-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/plugins/nucleo/fonts/nucleo-icons.eot -------------------------------------------------------------------------------- /client/src/assets/plugins/nucleo/fonts/nucleo-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/plugins/nucleo/fonts/nucleo-icons.ttf -------------------------------------------------------------------------------- /client/src/assets/plugins/nucleo/fonts/nucleo-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/plugins/nucleo/fonts/nucleo-icons.woff -------------------------------------------------------------------------------- /client/src/assets/plugins/nucleo/fonts/nucleo-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connorleee/BugTracker/ff8ed286ec72b32a04da6768208b3aab202c8e63/client/src/assets/plugins/nucleo/fonts/nucleo-icons.woff2 -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard-react.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Argon Dashboard React - v1.2.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/argon-dashboard-react 8 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 9 | * Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md) 10 | 11 | * Coded by Creative Tim 12 | 13 | ========================================================= 14 | 15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | */ 18 | 19 | // Core 20 | 21 | @import "argon-dashboard/custom/functions"; 22 | @import "argon-dashboard/custom/variables"; 23 | @import "argon-dashboard/custom/mixins"; 24 | 25 | // bootstrap (4.6.0) components 26 | 27 | @import "~bootstrap/scss/root"; 28 | @import "~bootstrap/scss/reboot"; 29 | @import "~bootstrap/scss/type"; 30 | @import "~bootstrap/scss/images"; 31 | @import "~bootstrap/scss/code"; 32 | @import "~bootstrap/scss/grid"; 33 | @import "~bootstrap/scss/tables"; 34 | @import "~bootstrap/scss/forms"; 35 | @import "~bootstrap/scss/buttons"; 36 | @import "~bootstrap/scss/transitions"; 37 | @import "~bootstrap/scss/dropdown"; 38 | @import "~bootstrap/scss/button-group"; 39 | @import "~bootstrap/scss/input-group"; 40 | @import "~bootstrap/scss/custom-forms"; 41 | @import "~bootstrap/scss/nav"; 42 | @import "~bootstrap/scss/navbar"; 43 | @import "~bootstrap/scss/card"; 44 | @import "~bootstrap/scss/breadcrumb"; 45 | @import "~bootstrap/scss/pagination"; 46 | @import "~bootstrap/scss/badge"; 47 | @import "~bootstrap/scss/jumbotron"; 48 | @import "~bootstrap/scss/alert"; 49 | @import "~bootstrap/scss/progress"; 50 | @import "~bootstrap/scss/media"; 51 | @import "~bootstrap/scss/list-group"; 52 | @import "~bootstrap/scss/close"; 53 | @import "~bootstrap/scss/modal"; 54 | @import "~bootstrap/scss/tooltip"; 55 | @import "~bootstrap/scss/popover"; 56 | @import "~bootstrap/scss/carousel"; 57 | @import "~bootstrap/scss/utilities"; 58 | @import "~bootstrap/scss/print"; 59 | 60 | // Argon utilities and components 61 | 62 | @import "argon-dashboard/custom/reboot"; 63 | @import "argon-dashboard/custom/utilities"; 64 | @import "argon-dashboard/custom/components"; 65 | 66 | // Vendor (Plugins) 67 | 68 | @import "argon-dashboard/custom/vendors"; 69 | 70 | // Docs components 71 | 72 | @import "argon-dashboard/docs/variables"; 73 | @import "argon-dashboard/docs/nav"; 74 | @import "argon-dashboard/docs/clipboard-js"; 75 | @import "argon-dashboard/docs/component-examples"; 76 | @import "argon-dashboard/docs/prism"; 77 | @import "argon-dashboard/docs/content"; 78 | @import "argon-dashboard/docs/sidebar"; 79 | @import "argon-dashboard/docs/footer"; 80 | 81 | // React Differences 82 | @import "react/react-differences"; 83 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Alert 3 | // 4 | 5 | @import "alerts/alert"; 6 | @import "alerts/alert-dismissible"; 7 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_avatar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Avatar 3 | // 4 | 5 | @import "avatars/avatar"; 6 | @import "avatars/avatar-group"; 7 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_badge.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badge 3 | // 4 | 5 | @import "badges/badge"; 6 | @import "badges/badge-circle"; 7 | @import "badges/badge-dot"; 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_buttons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Button 3 | // 4 | 5 | @import "buttons/button"; 6 | @import "buttons/button-icon"; 7 | @import "buttons/button-brand"; 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_card.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Cards 3 | // 4 | 5 | @import "cards/card"; 6 | @import "cards/card-profile"; 7 | @import "cards/card-animations"; 8 | @import "cards/card-stats"; 9 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_chart.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Chart 3 | // 4 | 5 | @import "charts/chart"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close 3 | // 4 | 5 | @import "close/close" 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_components.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Components 3 | // 4 | 5 | @import "alert"; 6 | @import "avatar"; 7 | @import "badge"; 8 | @import "buttons"; 9 | @import "card"; 10 | @import "chart"; 11 | @import "close"; 12 | @import "content"; 13 | @import "custom-forms"; 14 | @import "dropdown"; 15 | @import "footer"; 16 | @import "forms"; 17 | @import "header"; 18 | @import "icons"; 19 | @import "input-group"; 20 | @import "list-group"; 21 | @import "map"; 22 | @import "mask"; 23 | @import "modal"; 24 | @import "nav"; 25 | @import "navbar"; 26 | @import "pagination"; 27 | @import "popover"; 28 | @import "progress"; 29 | @import "separator"; 30 | @import "tables"; 31 | @import "type"; 32 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_content.scss: -------------------------------------------------------------------------------- 1 | .main-content { 2 | position: relative; 3 | 4 | // Navbar 5 | .navbar-top { 6 | position: absolute; 7 | left: 0; 8 | top: 0; 9 | width: 100%; 10 | z-index: 1; 11 | background-color: transparent; 12 | padding-left: 0 !important; 13 | padding-right: 0 !important; 14 | } 15 | 16 | // Container 17 | .container-fluid { 18 | @include media-breakpoint-up(md) { 19 | padding-left: ($main-content-padding-x + $grid-gutter-width / 2) !important; 20 | padding-right: ($main-content-padding-x + $grid-gutter-width / 2) !important; 21 | } 22 | } 23 | } 24 | 25 | 26 | // Offsets the main content depending on the sidebar positioning 27 | 28 | .navbar-vertical.navbar-expand { 29 | 30 | @each $breakpoint, 31 | $dimension in $grid-breakpoints { 32 | 33 | &-#{$breakpoint} { 34 | 35 | @include media-breakpoint-up(#{$breakpoint}) { 36 | 37 | // Left 38 | &.fixed-left + .main-content { 39 | margin-left: $navbar-vertical-width; 40 | } // Right 41 | &.fixed-right + .main-content { 42 | margin-right: $navbar-vertical-width; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_custom-forms.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom form 3 | // 4 | 5 | @import "custom-forms/custom-form"; 6 | @import "custom-forms/custom-control"; 7 | @import "custom-forms/custom-checkbox"; 8 | @import "custom-forms/custom-radio"; 9 | @import "custom-forms/custom-toggle"; 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_dropdown.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown 3 | // 4 | 5 | @import "dropdowns/dropdown"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_footer.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Footer 3 | // 4 | 5 | @import "footers/footer"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_forms.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Forms 3 | // 4 | 5 | @import "forms/form"; 6 | @import "forms/form-validation"; 7 | @import "forms/input-group"; 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_functions.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Functions 3 | // 4 | 5 | // Bootstrap default functions 6 | 7 | @import "~bootstrap/scss/functions"; 8 | 9 | 10 | // Retrieve color Sass maps 11 | 12 | @function section-color($key: "primary") { 13 | @return map-get($section-colors, $key); 14 | } 15 | 16 | 17 | // Lines colors 18 | 19 | @function shapes-primary-color($key: "step-1-gradient-bg") { 20 | @return map-get($shapes-primary-colors, $key); 21 | } 22 | 23 | @function shapes-default-color($key: "step-1-gradient-bg") { 24 | @return map-get($shapes-default-colors, $key); 25 | } 26 | 27 | @function lines-light-color($key: "step-1-gradient-bg") { 28 | @return map-get($shapes-light-colors, $key); 29 | } 30 | 31 | @function shapes-dark-color($key: "step-1-gradient-bg") { 32 | @return map-get($shapes-dark-colors, $key); 33 | } 34 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_header.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Header 3 | // 4 | 5 | @import "headers/header"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_icons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Icon 3 | // 4 | 5 | @import "icons/icon"; 6 | @import "icons/icon-shape"; 7 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_input-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Input group 3 | // 4 | 5 | @import "forms/input-group"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_list-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // List group 3 | // 4 | 5 | @import "list-groups/list-group"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_map.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Maps 3 | // 4 | 5 | @import "maps/map"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_mask.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Mask 3 | // 4 | 5 | @import "masks/mask"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_mixins.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Mixins 3 | // 4 | 5 | // Bootstrap default mixins 6 | 7 | @import "~bootstrap/scss/mixins"; 8 | 9 | 10 | // Custom mixins 11 | 12 | @import "mixins/alert"; 13 | @import "mixins/badge"; 14 | @import "mixins/background-variant"; 15 | @import "mixins/buttons"; 16 | @import "mixins/forms"; 17 | @import "mixins/icon"; 18 | @import "mixins/modals"; 19 | @import "mixins/popover"; 20 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_modal.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Modal 3 | // 4 | 5 | @import "modals/modal"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_nav.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Nav 3 | // 4 | 5 | 6 | @import "navs/nav"; 7 | @import "navs/nav-pills"; 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_navbar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Navbar 3 | // 4 | 5 | @import "navbars/navbar"; 6 | @import "navbars/navbar-vertical"; 7 | @import "navbars/navbar-search"; 8 | @import "navbars/navbar-dropdown"; 9 | @import "navbars/navbar-collapse"; 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_pagination.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination 3 | // 4 | 5 | @import "paginations/pagination"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_popover.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Popover 3 | // 4 | 5 | 6 | @import "popovers/popover"; 7 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_progress.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Progress 3 | // 4 | 5 | @import "progresses/progress"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_reboot.scss: -------------------------------------------------------------------------------- 1 | iframe { 2 | border: 0; 3 | } 4 | 5 | figcaption, 6 | figure, 7 | main { 8 | display: block; 9 | } 10 | 11 | main { 12 | overflow: hidden; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_section.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Sections 3 | // 4 | 5 | 6 | // Nucleo icons for presentation purpose 7 | 8 | .section-nucleo-icons { 9 | 10 | --icon-size: 5rem; 11 | --icon-sm-size: 3.75rem; 12 | --gutter: 7rem; 13 | 14 | .icons-container { 15 | position: relative; 16 | max-width: 100%; 17 | height: 360px; 18 | margin: 0 auto; 19 | z-index: 1; 20 | 21 | i { 22 | position: absolute; 23 | display: inline-flex; 24 | align-items: center; 25 | justify-content: center; 26 | border-radius: 50%; 27 | background: $white; 28 | z-index: 1; 29 | transform: translate(-50%, -50%); 30 | @include box-shadow($box-shadow); 31 | transition: all .2s cubic-bezier(.25,.65,.9,.75); 32 | 33 | &.icon { 34 | width: var(--icon-size); 35 | height: var(--icon-size); 36 | font-size: 1.7em; 37 | } 38 | 39 | &.icon-sm { 40 | width: var(--icon-sm-size); 41 | height: var(--icon-sm-size); 42 | font-size: 1.5em; 43 | } 44 | 45 | &:nth-child(1) { 46 | font-size: 42px; 47 | color: theme-color("warning"); 48 | z-index: 2; 49 | } 50 | } 51 | 52 | &:not(.on-screen) { 53 | i { 54 | transform: translate(-50%, -50%); 55 | left: 50%; 56 | top: 50%; 57 | 58 | &:not(:nth-child(1)) { 59 | opacity: 0; 60 | } 61 | } 62 | } 63 | 64 | &.on-screen { 65 | 66 | i { 67 | opacity: 1; 68 | 69 | &:nth-child(1) { 70 | left: 50%; 71 | top: 50%; 72 | font-size: 42px; 73 | color: theme-color("warning"); 74 | } 75 | 76 | &:nth-child(2) { 77 | left: calc(50% + (var(--gutter) * 1.7)); 78 | top: 50%; 79 | } 80 | 81 | &:nth-child(3) { 82 | left: calc(50% + var(--gutter)); 83 | top: calc(50% + var(--gutter)); 84 | } 85 | 86 | &:nth-child(4) { 87 | left: calc(50% + var(--gutter)); 88 | top: calc(50% - var(--gutter)); 89 | } 90 | 91 | &:nth-child(5) { 92 | left: calc(50% + (var(--gutter) * 4)); 93 | top: 50%; 94 | } 95 | 96 | &:nth-child(6) { 97 | left: calc(50% + (var(--gutter) * 2.7)); 98 | top: calc(50% + (var(--gutter) * 1.5)); 99 | } 100 | 101 | &:nth-child(7) { 102 | left: calc(50% + (var(--gutter) * 2.7)); 103 | top: calc(50% - (var(--gutter) * 1.5)); 104 | } 105 | 106 | &:nth-child(8) { 107 | left: calc(50% - (var(--gutter) * 1.7)); 108 | top: 50%; 109 | } 110 | 111 | &:nth-child(9) { 112 | left: calc(50% - var(--gutter)); 113 | top: calc(50% + var(--gutter)); 114 | } 115 | 116 | &:nth-child(10) { 117 | left: calc(50% - var(--gutter)); 118 | top: calc(50% - var(--gutter)); 119 | } 120 | 121 | &:nth-child(11) { 122 | left: calc(50% - (var(--gutter) * 4)); 123 | top: 50%; 124 | } 125 | 126 | &:nth-child(12) { 127 | left: calc(50% - (var(--gutter) * 2.7)); 128 | top: calc(50% + (var(--gutter) * 1.5)); 129 | } 130 | 131 | &:nth-child(13) { 132 | left: calc(50% - (var(--gutter) * 2.7)); 133 | top: calc(50% - (var(--gutter) * 1.5)); 134 | } 135 | } 136 | 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_separator.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Separator 3 | // 4 | 5 | @import "separators/separator"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_tables.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Tables 3 | // 4 | 5 | @import "tables/table"; 6 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_type.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Typography 3 | // 4 | 5 | @import "type/type"; 6 | @import "type/heading"; 7 | @import "type/display"; 8 | @import "type/article"; 9 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_utilities.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities 3 | // 4 | 5 | @import "utilities/backgrounds"; 6 | @import "utilities/floating"; 7 | @import "utilities/helper"; 8 | @import "utilities/image"; 9 | @import "utilities/opacity"; 10 | @import "utilities/overflow"; 11 | @import "utilities/position"; 12 | @import "utilities/sizing"; 13 | @import "utilities/spacing"; 14 | @import "utilities/shadows"; 15 | @import "utilities/text"; 16 | @import "utilities/transform"; 17 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/_vendors.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Vendors 3 | // include plugin styles 4 | // 5 | 6 | 7 | @import "vendors/bootstrap-datepicker"; 8 | @import "vendors/nouislider"; 9 | @import "vendors/scrollbar"; 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/alerts/_alert-dismissible.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dismissible alert 3 | // 4 | 5 | .alert-dismissible { 6 | .close { 7 | top: 50%; 8 | right: $alert-padding-x; 9 | padding: 0; 10 | transform: translateY(-50%); 11 | color: rgba($white, .6); 12 | opacity: 1; 13 | 14 | &:hover, 15 | &:focus { 16 | color: rgba($white, .9); 17 | opacity: 1 !important; 18 | } 19 | 20 | @include media-breakpoint-down(xs) { 21 | top: 1rem; 22 | right: .5rem; 23 | } 24 | 25 | &>span:not(.sr-only) { 26 | font-size: 1.5rem; 27 | background-color: transparent; 28 | color: rgba($white, .6); 29 | } 30 | 31 | &:hover, 32 | &:focus { 33 | &>span:not(.sr-only) { 34 | background-color: transparent; 35 | color: rgba($white, .9); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/alerts/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Alert 3 | // 4 | 5 | .alert { 6 | font-size: $font-size-sm; 7 | } 8 | 9 | // Alert heading 10 | 11 | .alert-heading { 12 | font-weight: $font-weight-bold; 13 | font-size: $h4-font-size; 14 | margin-top: .15rem; 15 | } 16 | 17 | 18 | // Alert icon 19 | .alert-icon { 20 | font-size: 1.25rem; 21 | margin-right: 1.25rem; 22 | display: inline-block; 23 | vertical-align: middle; 24 | 25 | i.ni { 26 | position: relative; 27 | top: 1px; 28 | } 29 | } 30 | 31 | 32 | // Alert text next to an alert icon 33 | .alert-text { 34 | display: inline-block; 35 | vertical-align: middle; 36 | } 37 | 38 | 39 | // Alert links 40 | 41 | [class*="alert-"] { 42 | .alert-link { 43 | color: $white; 44 | border-bottom: 1px dotted rgba($white, .5); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/avatars/_avatar-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Avatar group 3 | // 4 | 5 | // General styles 6 | 7 | .avatar-group { 8 | .avatar { 9 | position: relative; 10 | z-index: 2; 11 | border: 2px solid $card-bg; 12 | 13 | &:hover { 14 | z-index: 3; 15 | } 16 | } 17 | 18 | .avatar + .avatar { 19 | margin-left: -1rem; 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/avatars/_avatar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Avatar 3 | // 4 | 5 | // General styles 6 | 7 | .avatar { 8 | color: $white; 9 | background-color: $gray-500; 10 | display: inline-flex; 11 | align-items: center; 12 | justify-content: center; 13 | font-size: 1rem; 14 | border-radius: 50%; 15 | height: 48px; 16 | width: 48px; 17 | 18 | img { 19 | width: 100%; 20 | border-radius: 50%; 21 | } 22 | 23 | + .avatar-content { 24 | display: inline-block; 25 | margin-left: .75rem; 26 | } 27 | } 28 | 29 | 30 | // Avatar size variations 31 | 32 | .avatar-lg { 33 | width: 58px; 34 | height: 58px; 35 | font-size: $font-size-sm; 36 | } 37 | 38 | .avatar-sm { 39 | width: 36px; 40 | height: 36px; 41 | font-size: $font-size-sm; 42 | } 43 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/badges/_badge-circle.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Circle badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge-circle { 9 | text-align: center; 10 | display: inline-flex; 11 | align-items: center; 12 | justify-content: center; 13 | border-radius: 50%; 14 | width: 2rem; 15 | height: 2rem; 16 | font-size: .875rem; 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/badges/_badge-dot.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dot badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge-dot { 9 | padding-left: 0; 10 | padding-right: 0; 11 | background: transparent; 12 | font-weight: $font-weight-normal; 13 | font-size: $font-size-sm; 14 | text-transform: none; 15 | 16 | strong { 17 | color: $gray-800; 18 | } 19 | 20 | i { 21 | display: inline-block; 22 | vertical-align: middle; 23 | width: .375rem; 24 | height: .375rem; 25 | border-radius: 50%; 26 | margin-right: .375rem; 27 | } 28 | 29 | &.badge-md { 30 | i { 31 | width: .5rem; 32 | height: .5rem; 33 | } 34 | } 35 | 36 | &.badge-lg { 37 | i { 38 | width: .625rem; 39 | height: .625rem; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/badges/_badge.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge { 9 | text-transform: $badge-text-transfom; 10 | 11 | a { 12 | color: $white; 13 | } 14 | } 15 | 16 | 17 | // Size variations 18 | 19 | .badge-md { 20 | padding: .65em 1em; 21 | } 22 | 23 | .badge-lg { 24 | padding: .85em 1.375em; 25 | } 26 | 27 | 28 | // Multiple inline badges 29 | 30 | .badge-inline { 31 | margin-right: .625rem; 32 | 33 | + span { 34 | top: 2px; 35 | position: relative; 36 | 37 | > a { 38 | text-decoration: underline; 39 | } 40 | } 41 | } 42 | 43 | 44 | // Badge spacing inside a btn with some text 45 | 46 | .btn { 47 | .badge { 48 | &:not(:first-child) { 49 | margin-left: .5rem; 50 | } 51 | &:not(:last-child) { 52 | margin-right: .5rem; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/buttons/_button-brand.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Brand buttons 3 | // 4 | 5 | 6 | // Color variations 7 | 8 | @each $color, $value in $brand-colors { 9 | .btn-#{$color} { 10 | @include button-variant($value, $value); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/buttons/_button-icon.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Icon buttons 3 | // 4 | 5 | .btn-icon { 6 | .btn-inner--icon { 7 | img { 8 | width: 20px; 9 | } 10 | } 11 | 12 | .btn-inner--text:not(:first-child) { 13 | margin-left: 0.75em; 14 | } 15 | 16 | .btn-inner--text:not(:last-child) { 17 | margin-right: 0.75em; 18 | } 19 | } 20 | 21 | 22 | // Button only with icon and NO text 23 | 24 | .btn-icon-only { 25 | width: 2.375rem; 26 | height: 2.375rem; 27 | padding: 0; 28 | } 29 | 30 | a.btn-icon-only { 31 | line-height: 2.5; 32 | } 33 | 34 | .btn-icon-only.btn-sm { 35 | width: 2rem; 36 | height: 2rem; 37 | } 38 | 39 | 40 | // 41 | // Clipboard button 42 | // dedicated element for copying icons 43 | // 44 | 45 | .btn-icon-clipboard { 46 | margin: 0; 47 | padding: 1.5rem; 48 | font-size: $font-size-base; 49 | font-weight: $font-weight-normal; 50 | line-height: 1.25; 51 | color: $gray-800; 52 | background-color: $gray-100; 53 | border-radius: $border-radius; 54 | border: 0; 55 | text-align: left; 56 | font-family: inherit; 57 | display: inline-block; 58 | vertical-align: middle; 59 | text-decoration: none; 60 | -moz-appearance: none; 61 | cursor: pointer; 62 | width: 100%; 63 | margin: .5rem 0; 64 | 65 | &:hover { 66 | background-color: $white; 67 | box-shadow: rgba(0, 0, 0, .1) 0 0 0 1px, rgba(0, 0, 0, .1) 0 4px 16px; 68 | } 69 | 70 | > div { 71 | align-items: center; 72 | display: flex; 73 | } 74 | 75 | i { 76 | box-sizing: content-box; 77 | color: theme-color("primary"); 78 | vertical-align: middle; 79 | font-size: 1.5rem; 80 | } 81 | 82 | span { 83 | display: inline-block; 84 | font-size: 0.875rem; 85 | line-height: 1.5; 86 | margin-left: 16px; 87 | overflow: hidden; 88 | white-space: nowrap; 89 | text-overflow: ellipsis; 90 | vertical-align: middle; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/buttons/_button.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Icon buttons 3 | // 4 | 5 | // General styles 6 | 7 | .btn { 8 | position: relative; 9 | text-transform: $btn-text-transform; 10 | transition: $transition-base; 11 | letter-spacing: $btn-letter-spacing; 12 | font-size: $input-btn-font-size; 13 | will-change: transform; 14 | 15 | &:hover { 16 | @include box-shadow($btn-hover-box-shadow); 17 | transform: translateY($btn-hover-translate-y); 18 | } 19 | 20 | &:not(:last-child) { 21 | margin-right: .5rem; 22 | } 23 | 24 | 25 | // Icons 26 | 27 | i:not(:first-child), 28 | svg:not(:first-child) { 29 | margin-left: .5rem; 30 | } 31 | 32 | i:not(:last-child), 33 | svg:not(:last-child) { 34 | margin-right: .5rem; 35 | } 36 | } 37 | 38 | 39 | // Remove translateY and margin animation when btn is included in a btn-group or input-group 40 | 41 | .btn-group, 42 | .input-group { 43 | .btn { 44 | margin-right: 0; 45 | transform: translateY(0); 46 | } 47 | } 48 | 49 | 50 | // Size variations 51 | 52 | .btn-sm { 53 | font-size: $input-btn-font-size-sm; 54 | } 55 | 56 | .btn-lg { 57 | font-size: $input-btn-font-size-lg; 58 | } 59 | 60 | 61 | // Some quick fixes (to revise) 62 | 63 | // Fixes 64 | [class*="btn-outline-"] { 65 | border-width: 1px; 66 | } 67 | 68 | .btn-outline-secondary { 69 | color: darken(theme-color("secondary"), 50%); 70 | } 71 | 72 | .btn-inner--icon { 73 | i:not(.fa) { 74 | position: relative; 75 | top: 2px; 76 | } 77 | } 78 | 79 | .btn-link { 80 | font-weight: $btn-font-weight; 81 | box-shadow: none; 82 | 83 | &:hover { 84 | box-shadow: none; 85 | transform: none; 86 | } 87 | } 88 | 89 | .btn-neutral { 90 | color: theme-color("primary"); 91 | } 92 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/cards/_card-animations.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Card with hover animations 3 | // 4 | 5 | .card-lift--hover { 6 | &:hover { 7 | transform: translateY(-20px); 8 | @include transition($transition-base); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/cards/_card-blockquote.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Card with blockquote 3 | // 4 | 5 | .card-blockquote { 6 | padding: 2rem; 7 | position: relative; 8 | 9 | .svg-bg { 10 | display: block; 11 | width: 100%; 12 | height: 95px; 13 | position: absolute; 14 | top: -94px; 15 | left: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/cards/_card-profile.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Profile card 3 | // 4 | 5 | .card-profile-image { 6 | position: relative; 7 | 8 | img { 9 | max-width: 180px; 10 | border-radius: $border-radius; 11 | @extend .shadow; 12 | transform: translate(-50%,-30%); 13 | position: absolute; 14 | left: 50%; 15 | transition: $transition-base; 16 | 17 | &:hover { 18 | transform: translate(-50%, -33%); 19 | } 20 | } 21 | } 22 | 23 | .card-profile-stats { 24 | padding: 1rem 0; 25 | 26 | > div { 27 | text-align: center; 28 | margin-right: 1rem; 29 | padding: .875rem; 30 | 31 | &:last-child { 32 | margin-right: 0; 33 | } 34 | 35 | .heading { 36 | font-size: 1.1rem; 37 | font-weight: bold; 38 | display: block; 39 | } 40 | .description { 41 | font-size: .875rem; 42 | color: $gray-500; 43 | } 44 | } 45 | } 46 | 47 | .card-profile-actions { 48 | padding: .875rem; 49 | } 50 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/cards/_card-stats.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Card stats 3 | // 4 | 5 | .card-stats { 6 | .card-body { 7 | padding: 1rem 1.5rem; 8 | } 9 | 10 | .card-status-bullet { 11 | position: absolute; 12 | top: 0; 13 | right: 0; 14 | transform: translate(50%, -50%); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/cards/_card.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Card 3 | // 4 | 5 | 6 | .card-translucent { 7 | background-color: rgba(18, 91, 152, 0.08); 8 | } 9 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/charts/_chart.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Chart 3 | // 4 | 5 | .chart { 6 | position: relative; 7 | height: $chart-height; 8 | } 9 | 10 | 11 | // Size variations 12 | 13 | .chart-sm { 14 | height: $chart-height-sm; 15 | } 16 | 17 | 18 | // Legend 19 | 20 | .chart-legend { 21 | display: flex; 22 | justify-content: center; 23 | margin-top: $chart-legend-margin-top; 24 | font-size: $chart-legend-font-size; 25 | text-align: center; 26 | color: $chart-legend-color; 27 | } 28 | 29 | .chart-legend-item { 30 | display: inline-flex; 31 | align-items: center; 32 | 33 | + .chart-legend-item { 34 | margin-left: 1rem; 35 | } 36 | } 37 | 38 | .chart-legend-indicator { 39 | display: inline-block; 40 | width: 0.5rem; 41 | height: 0.5rem; 42 | margin-right: 0.375rem; 43 | border-radius: 50%; 44 | } 45 | 46 | 47 | // Tooltip 48 | 49 | #chart-tooltip { 50 | z-index: 0; 51 | 52 | .arrow { 53 | top: 100%; 54 | left: 50%; 55 | transform: translateX(-50%) translateX(-.5rem); 56 | } 57 | } 58 | 59 | 60 | // Chart info overlay 61 | 62 | .chart-info-overlay { 63 | position: absolute; 64 | top: 0; 65 | left: 5%; 66 | max-width: 350px; 67 | padding: 20px; 68 | z-index: 1; 69 | } 70 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/close/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close 3 | // 4 | 5 | .close { 6 | @if $enable-transitions { 7 | transition: $transition-base; 8 | } 9 | 10 | &>span:not(.sr-only) { 11 | background-color: $close-bg; 12 | color: $close-color; 13 | line-height: 17px; 14 | height: 1.25rem; 15 | width: 1.25rem; 16 | border-radius: 50%; 17 | font-size: 1.25rem; 18 | display: block; 19 | @if $enable-transitions { 20 | transition: $transition-base; 21 | } 22 | } 23 | 24 | &:hover, 25 | &:focus { 26 | background-color: $close-hover-bg; 27 | color: $close-hover-color; 28 | outline: none; 29 | 30 | span:not(.sr-only) { 31 | background-color: $close-hover-bg; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/custom-forms/_custom-checkbox.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom checkbox 3 | // 4 | 5 | .custom-checkbox { 6 | .custom-control-input ~ .custom-control-label { 7 | cursor: pointer; 8 | font-size: $font-size-sm; 9 | } 10 | 11 | .custom-control-input { 12 | &:checked { 13 | ~ .custom-control-label { 14 | &::before { 15 | border-color: $custom-control-indicator-checked-border-color; 16 | } 17 | &::after { 18 | background-image: $custom-checkbox-indicator-icon-checked; 19 | } 20 | } 21 | } 22 | 23 | &:disabled { 24 | ~ .custom-control-label { 25 | &::before { 26 | border-color: $custom-control-indicator-disabled-bg; 27 | } 28 | } 29 | 30 | &:checked { 31 | &::before { 32 | border-color: $custom-control-indicator-checked-disabled-bg; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/custom-forms/_custom-control.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom control 3 | // additional styles for custom checkboxes, radios and other 4 | // 5 | 6 | .custom-control-label { 7 | // Background-color and (when enabled) gradient 8 | &::before { 9 | border: $custom-control-indicator-border-width solid $custom-control-indicator-border-color; 10 | @if $enable-transitions { 11 | transition: $input-transition; 12 | } 13 | } 14 | 15 | span { 16 | position: relative; 17 | top: 2px; 18 | } 19 | } 20 | 21 | .custom-control-label { 22 | margin-bottom: 0; 23 | } 24 | 25 | 26 | // Alternative style 27 | 28 | .custom-control-alternative { 29 | .custom-control-label { 30 | // Background-color and (when enabled) gradient 31 | &::before { 32 | border: 0; 33 | box-shadow: $input-alternative-box-shadow; 34 | } 35 | } 36 | 37 | .custom-control-input { 38 | &:checked { 39 | ~ .custom-control-label { 40 | &::before { 41 | box-shadow: $input-focus-alternative-box-shadow; 42 | } 43 | } 44 | } 45 | 46 | &:active~.custom-control-label::before, 47 | &:focus~.custom-control-label::before { 48 | box-shadow: $input-alternative-box-shadow; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/custom-forms/_custom-form.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom checkbox 3 | // 4 | 5 | .custom-checkbox { 6 | .custom-control-input ~ .custom-control-label { 7 | cursor: pointer; 8 | font-size: $font-size-sm; 9 | } 10 | 11 | .custom-control-input { 12 | &:checked { 13 | ~ .custom-control-label { 14 | &::before { 15 | border-color: $custom-control-indicator-checked-border-color; 16 | } 17 | &::after { 18 | background-image: $custom-checkbox-indicator-icon-checked; 19 | } 20 | } 21 | } 22 | 23 | &:disabled { 24 | ~ .custom-control-label { 25 | &::before { 26 | border-color: $custom-control-indicator-disabled-bg; 27 | } 28 | } 29 | 30 | &:checked { 31 | &::before { 32 | border-color: $custom-control-indicator-checked-disabled-bg; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/custom-forms/_custom-radio.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom radio 3 | // 4 | 5 | .custom-radio { 6 | .custom-control-input ~ .custom-control-label { 7 | cursor: pointer; 8 | font-size: $font-size-sm; 9 | } 10 | 11 | .custom-control-input { 12 | &:checked { 13 | ~ .custom-control-label { 14 | &::before { 15 | border-color: $custom-control-indicator-checked-border-color; 16 | } 17 | &::after { 18 | background-image: $custom-radio-indicator-icon-checked; 19 | } 20 | } 21 | } 22 | 23 | &:disabled { 24 | ~ .custom-control-label { 25 | &::before { 26 | border-color: $custom-control-indicator-disabled-bg; 27 | } 28 | } 29 | 30 | &:checked { 31 | &::before { 32 | border-color: $custom-control-indicator-checked-disabled-bg; 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/custom-forms/_custom-toggle.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom toggle 3 | // 4 | 5 | .custom-toggle { 6 | position: relative; 7 | display: inline-block; 8 | width: $custom-toggle-width; 9 | height: 1.5rem; 10 | 11 | input { 12 | display: none; 13 | 14 | &:checked { 15 | + .custom-toggle-slider { 16 | border: $custom-control-indicator-border-width solid $custom-control-indicator-checked-border-color; 17 | 18 | &:before { 19 | background: $custom-toggle-checked-bg; 20 | transform: translateX(1.625rem); 21 | } 22 | } 23 | } 24 | 25 | &:disabled { 26 | + .custom-toggle-slider { 27 | border: $custom-control-indicator-border-width solid $custom-control-indicator-disabled-bg; 28 | } 29 | 30 | &:checked { 31 | + .custom-toggle-slider { 32 | border: $custom-control-indicator-border-width solid $custom-control-indicator-disabled-bg; 33 | 34 | &:before { 35 | background-color: lighten($custom-control-indicator-checked-bg, 10%); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | .custom-toggle-slider { 44 | position: absolute; 45 | cursor: pointer; 46 | top: 0; 47 | left: 0; 48 | right: 0; 49 | bottom: 0; 50 | border: $custom-control-indicator-border-width solid $input-border-color; 51 | border-radius: 34px !important; 52 | background-color: transparent; 53 | 54 | 55 | &:before { 56 | position: absolute; 57 | content: ""; 58 | height: 18px; 59 | width: 18px; 60 | left: 2px; 61 | bottom: 2px; 62 | border-radius: 50% !important; 63 | background-color: $custom-toggle-slider-bg; 64 | transition: $input-transition; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/dropdowns/_dropdown.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown 3 | // 4 | 5 | // General styles 6 | 7 | .dropdown, 8 | .dropup, 9 | .dropright, 10 | .dropleft { 11 | display: inline-block; 12 | } 13 | 14 | .dropdown-menu { 15 | min-width: 12rem; 16 | 17 | .dropdown-item { 18 | padding: .5rem 1rem; 19 | font-size: $font-size-sm; 20 | > i, 21 | > svg { 22 | margin-right: 1rem; 23 | font-size: 1rem; 24 | vertical-align: -17%; 25 | } 26 | } 27 | } 28 | 29 | .dropdown-header { 30 | padding-left: 1rem; 31 | padding-right: 1rem; 32 | color: $gray-100; 33 | font-size: .625rem; 34 | text-transform: uppercase; 35 | font-weight: 700; 36 | } 37 | 38 | 39 | // Media components inside dropdown link 40 | 41 | .dropdown-menu { 42 | a.media { 43 | 44 | > div { 45 | &:first-child { 46 | line-height: 1; 47 | } 48 | } 49 | 50 | p { 51 | color: $gray-600; 52 | } 53 | 54 | &:hover { 55 | .heading, 56 | p { 57 | color: theme-color("default") !important; 58 | } 59 | } 60 | } 61 | } 62 | 63 | 64 | // Size variations 65 | 66 | .dropdown-menu-sm { 67 | min-width: 100px; 68 | border: $border-radius-lg; 69 | } 70 | 71 | .dropdown-menu-lg { 72 | min-width: 260px; 73 | border-radius: $border-radius-lg; 74 | } 75 | 76 | .dropdown-menu-xl { 77 | min-width: 450px; 78 | border-radius: $border-radius-lg; 79 | } 80 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/footers/_footer.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Footer 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .footer { 9 | background: $footer-bg; 10 | padding: $footer-padding-y $footer-padding-x; 11 | 12 | .col-footer { 13 | .heading { 14 | color: $footer-heading-color; 15 | letter-spacing: 0; 16 | font-size: $footer-heading-font-size; 17 | text-transform: uppercase; 18 | font-weight: $font-weight-bold; 19 | margin-bottom: 1rem; 20 | } 21 | } 22 | 23 | .nav .nav-item .nav-link, 24 | .footer-link { 25 | color: $footer-link-color !important; 26 | 27 | &:hover { 28 | color: $footer-link-hover-color !important; 29 | } 30 | } 31 | 32 | .list-unstyled li a { 33 | display: inline-block; 34 | padding: .125rem 0; 35 | color: $footer-link-color; 36 | font-size: $footer-link-font-size; 37 | 38 | &:hover { 39 | color: $footer-link-hover-color; 40 | } 41 | } 42 | 43 | .copyright { 44 | font-size: $font-size-sm; 45 | } 46 | } 47 | 48 | 49 | // Dark footer 50 | 51 | .footer-dark { 52 | .col-footer .heading { 53 | color: $white; 54 | } 55 | } 56 | 57 | 58 | // Footer nav used for copyright and some links, but not limited to this 59 | 60 | .nav-footer { 61 | .nav-link { 62 | font-size: $font-size-sm; 63 | } 64 | 65 | .nav-item:last-child { 66 | .nav-link { 67 | padding-right: 0; 68 | } 69 | } 70 | } 71 | 72 | 73 | // Footer with cards over 74 | 75 | .footer.has-cards { 76 | overflow: hidden; 77 | padding-top: 500px; 78 | margin-top: -420px; 79 | position: relative; 80 | background: transparent; 81 | pointer-events: none; 82 | 83 | &:before { 84 | content: ""; 85 | position: absolute; 86 | left: 0; 87 | right: 0; 88 | top: 600px; 89 | height: 2000px; 90 | background: theme-color("secondary"); 91 | transform: skew(0,-8deg); 92 | } 93 | 94 | .container { 95 | pointer-events: auto; 96 | position: relative; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/forms/_form-validation.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Form validation 3 | // 4 | 5 | // Validation 6 | 7 | .has-success, 8 | .has-danger { 9 | position: relative; 10 | 11 | &:after, { 12 | width: 19px; 13 | height: 19px; 14 | line-height: 19px; 15 | text-align: center; 16 | font-family: 'NucleoIcons'; 17 | display: inline-block; 18 | position: absolute; 19 | right: 15px; 20 | top: 2px; 21 | transform: translateY(50%); 22 | border-radius: 50%; 23 | font-size: 9px; 24 | opacity: 1; 25 | } 26 | } 27 | 28 | .has-success { 29 | &:after { 30 | content: "\ea26"; 31 | color: daken($form-feedback-valid-color, 18%); 32 | background-color: $form-feedback-valid-bg; 33 | } 34 | 35 | .form-control { 36 | background-color: $input-focus-bg; 37 | 38 | &:focus { 39 | border-color: $input-focus-border-color; 40 | } 41 | 42 | 43 | // Placeholder 44 | 45 | &::placeholder { 46 | color: $form-feedback-valid-color; 47 | } 48 | } 49 | } 50 | 51 | .has-danger { 52 | &:after { 53 | content: "\ea53"; 54 | color: daken($form-feedback-invalid-color, 18%); 55 | background-color: $form-feedback-invalid-bg; 56 | } 57 | 58 | .form-control { 59 | background-color: $input-focus-bg; 60 | 61 | &:focus { 62 | border-color: $input-focus-border-color; 63 | } 64 | 65 | // Placeholder 66 | 67 | &::placeholder { 68 | color: $form-feedback-invalid-color; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/forms/_form.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Forms 3 | // 4 | 5 | 6 | // Labels 7 | 8 | .form-control-label { 9 | color: $gray-700; 10 | font-size: $font-size-sm; 11 | font-weight: $font-weight-bold; 12 | } 13 | 14 | 15 | // Text inputs 16 | 17 | .form-control { 18 | font-size: $input-btn-font-size; 19 | 20 | &:focus { 21 | &::placeholder { 22 | color: $input-focus-placeholder-color; 23 | } 24 | } 25 | } 26 | 27 | 28 | // Textarea 29 | 30 | textarea[resize="none"] { 31 | resize: none!important; 32 | } 33 | 34 | textarea[resize="both"] { 35 | resize: both!important; 36 | } 37 | 38 | textarea[resize="vertical"] { 39 | resize: vertical!important; 40 | } 41 | 42 | textarea[resize="horizontal"] { 43 | resize: horizontal!important; 44 | } 45 | 46 | 47 | // Form input variations 48 | 49 | // Muted input 50 | 51 | .form-control-muted { 52 | background-color: $input-muted-bg; 53 | border-color: $input-muted-bg; 54 | box-shadow: none; 55 | 56 | &:focus { 57 | background-color: $input-focus-muted-bg; 58 | } 59 | } 60 | 61 | 62 | // Alternative input 63 | 64 | .form-control-alternative { 65 | box-shadow: $input-alternative-box-shadow; 66 | border: 0; 67 | transition: box-shadow .15s ease; 68 | 69 | &:focus { 70 | box-shadow: $input-focus-alternative-box-shadow; 71 | } 72 | } 73 | 74 | // Size variations: Fixes to the bootstrap defaults 75 | 76 | .form-control-lg { 77 | font-size: $font-size-base; 78 | } 79 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/forms/_input-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Input group 3 | // 4 | 5 | .input-group { 6 | box-shadow: $input-box-shadow; 7 | border-radius: $input-border-radius; 8 | transition: $transition-base; 9 | 10 | .form-control { 11 | box-shadow: none; 12 | 13 | &:not(:first-child) { 14 | border-left: 0; 15 | padding-left: 0; 16 | } 17 | &:not(:last-child) { 18 | border-right: 0; 19 | padding-right: 0; 20 | } 21 | &:focus { 22 | box-shadow: none; 23 | } 24 | } 25 | } 26 | 27 | .input-group-text { 28 | transition: $input-transition; 29 | } 30 | 31 | 32 | // Alternative input groups related to .form-control-alternative 33 | 34 | 35 | .input-group-alternative { 36 | box-shadow: $input-alternative-box-shadow; 37 | border: 0; 38 | transition: box-shadow .15s ease; 39 | 40 | .form-control, 41 | .input-group-text { 42 | border: 0; 43 | box-shadow: none; 44 | } 45 | } 46 | 47 | .focused { 48 | .input-group-alternative { 49 | box-shadow: $input-focus-alternative-box-shadow !important; 50 | } 51 | } 52 | 53 | 54 | // .focus class is applied dinamycally from theme.js 55 | 56 | .focused { 57 | .input-group { 58 | box-shadow: $input-focus-box-shadow; 59 | } 60 | 61 | .input-group-text { 62 | color: $input-group-addon-focus-color; 63 | background-color: $input-group-addon-focus-bg; 64 | border-color: $input-group-addon-focus-border-color; 65 | } 66 | 67 | .form-control { 68 | border-color: $input-group-addon-focus-border-color; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/headers/_header.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Header 3 | // 4 | 5 | .header { 6 | position: relative; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/icons/_icon-shape.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Icon shape 3 | // 4 | 5 | 6 | .icon-shape { 7 | padding: 12px; 8 | text-align: center; 9 | display: inline-flex; 10 | align-items: center; 11 | justify-content: center; 12 | border-radius: 50%; 13 | 14 | 15 | i, svg { 16 | font-size: 1.25rem; 17 | } 18 | 19 | &.icon-lg { 20 | i, svg { 21 | font-size: 1.625rem; 22 | } 23 | } 24 | 25 | &.icon-sm { 26 | i, svg { 27 | font-size: .875rem; 28 | } 29 | } 30 | 31 | svg { 32 | width: 30px; 33 | height: 30px; 34 | } 35 | 36 | } 37 | 38 | @each $color, $value in $theme-colors { 39 | .icon-shape-#{$color} { 40 | @include icon-shape-variant(theme-color($color)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/icons/_icon.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Icon 3 | // 4 | 5 | .icon { 6 | width: $icon-size; 7 | height: $icon-size; 8 | 9 | i, svg { 10 | font-size: $icon-size - .75; 11 | } 12 | 13 | + .icon-text { 14 | padding-left: 1rem; 15 | width: calc(100% - #{$icon-size} - 1); 16 | } 17 | } 18 | 19 | 20 | // Extra large icons 21 | 22 | .icon-xl { 23 | width: $icon-size-xl; 24 | height: $icon-size-xl; 25 | 26 | i, svg { 27 | font-size: $icon-size-xl - .75; 28 | } 29 | 30 | + .icon-text { 31 | width: calc(100% - #{$icon-size-xl} - 1); 32 | } 33 | } 34 | 35 | 36 | // Large icons 37 | 38 | .icon-lg { 39 | width: $icon-size-lg; 40 | height: $icon-size-lg; 41 | 42 | i, svg { 43 | font-size: $icon-size-lg - .75; 44 | } 45 | 46 | + .icon-text { 47 | width: calc(100% - #{$icon-size-lg} - 1); 48 | } 49 | } 50 | 51 | 52 | // Small icon 53 | 54 | .icon-sm { 55 | width: $icon-size-sm; 56 | height: $icon-size-sm; 57 | 58 | i, svg { 59 | font-size: $icon-size-sm - .75; 60 | } 61 | 62 | + .icon-text { 63 | width: calc(100% - #{$icon-size-sm} - 1); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/list-groups/_list-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // List group 3 | // 4 | 5 | 6 | // Space list items 7 | 8 | .list-group-space { 9 | .list-group-item { 10 | margin-bottom: 1.5rem; 11 | @include border-radius($list-group-border-radius); 12 | } 13 | } 14 | 15 | 16 | // Extended list group components 17 | 18 | .list-group-img { 19 | width: 3rem; 20 | height: 3rem; 21 | border-radius: 50%; 22 | vertical-align: top; 23 | margin: -.1rem 1.2rem 0 -.2rem; 24 | } 25 | 26 | .list-group-content { 27 | flex: 1; 28 | min-width: 0; 29 | 30 | > p { 31 | color: $gray-500; 32 | line-height: 1.5; 33 | margin: .2rem 0 0; 34 | } 35 | } 36 | 37 | .list-group-heading { 38 | font-size: $font-size-base; 39 | color: $gray-800; 40 | 41 | > small { 42 | float: right; 43 | color: $gray-500; 44 | font-weight: 500; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/maps/_map.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Map 3 | // 4 | 5 | .map-canvas { 6 | position: relative; 7 | width: 100%; 8 | height: $map-height; 9 | border-radius: $border-radius; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/masks/_mask.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Mask 3 | // 4 | 5 | .mask { 6 | position: absolute; 7 | top: 0; 8 | left: 0; 9 | width: 100%; 10 | height: 100%; 11 | @include transition($transition-base); 12 | } 13 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_alert.scss: -------------------------------------------------------------------------------- 1 | @mixin alert-variant($background, $border, $color) { 2 | color: color-yiq($background); 3 | @include gradient-bg($background); 4 | border-color: $border; 5 | 6 | hr { 7 | border-top-color: darken($border, 5%); 8 | } 9 | 10 | .alert-link { 11 | color: darken($color, 10%); 12 | } 13 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_background-variant.scss: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | @mixin bg-variant($parent, $color, $ignore-warning: true) { 3 | #{$parent} { 4 | background-color: $color !important; 5 | } 6 | a#{$parent}, 7 | button#{$parent} { 8 | @include hover-focus { 9 | background-color: darken($color, 10%) !important; 10 | } 11 | } 12 | } 13 | 14 | @mixin bg-gradient-variant($parent, $color) { 15 | #{$parent} { 16 | background: linear-gradient(87deg, $color 0, adjust-hue($color, 25%) 100%) !important; 17 | } 18 | } 19 | 20 | @mixin bg-translucent-variant($parent, $color) { 21 | #{$parent} { 22 | background-color: darken(rgba($color, $translucent-color-opacity), 7%) !important; 23 | } 24 | a#{$parent}, 25 | button#{$parent} { 26 | @include hover-focus { 27 | background-color: darken(rgba($color, $translucent-color-opacity), 12%) !important; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_badge.scss: -------------------------------------------------------------------------------- 1 | @mixin badge-variant($bg) { 2 | color: saturate(darken($bg, 10%), 10); 3 | background-color: transparentize(lighten($bg, 25%), .5); 4 | 5 | &[href] { 6 | @include hover-focus { 7 | color: color-yiq($bg); 8 | text-decoration: none; 9 | background-color: darken($bg, 10%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | @mixin button-variant($background, $border, $hover-background: darken($background, 0%), $hover-border: darken($border, 0%), $active-background: darken($background, 10%), $active-border: darken($border, 0%)) { 2 | color: color-yiq($background); 3 | @include gradient-bg($background); 4 | border-color: $border; 5 | @include box-shadow($btn-box-shadow); 6 | 7 | @include hover { 8 | color: color-yiq($hover-background); 9 | @include gradient-bg($hover-background); 10 | border-color: $hover-border; 11 | } 12 | 13 | &:focus, 14 | &.focus { 15 | // Avoid using mixin so we can pass custom focus shadow properly 16 | @if $enable-shadows { 17 | box-shadow: $btn-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5); 18 | } 19 | @else { 20 | box-shadow: 0 0 0 $btn-focus-width rgba($border, .5); 21 | } 22 | } // Disabled comes first so active can properly restyle 23 | &.disabled, 24 | &:disabled { 25 | color: color-yiq($background); 26 | background-color: $background; 27 | border-color: $border; 28 | } 29 | 30 | &:not(:disabled):not(.disabled):active, 31 | &:not(:disabled):not(.disabled).active, 32 | .show>&.dropdown-toggle { 33 | color: color-yiq($active-background); 34 | background-color: $active-background; 35 | @if $enable-gradients { 36 | background-image: none; // Remove the gradient for the pressed/active state 37 | } 38 | border-color: $active-border; 39 | 40 | &:focus { 41 | // Avoid using mixin so we can pass custom focus shadow properly 42 | @if $enable-shadows { 43 | box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($border, .5); 44 | } 45 | @else { 46 | box-shadow: 0 0 0 $btn-focus-width rgba($border, .5); 47 | } 48 | } 49 | } 50 | } 51 | 52 | @mixin button-outline-variant($color, $color-hover: color-yiq($color), $active-background: $color, $active-border: $color) { 53 | color: $color; 54 | background-color: transparent; 55 | background-image: none; 56 | border-color: $color; 57 | 58 | &:hover { 59 | color: $color-hover; 60 | background-color: $active-background; 61 | border-color: $active-border; 62 | } 63 | 64 | &:focus, 65 | &.focus { 66 | box-shadow: 0 0 0 $btn-focus-width rgba($color, .5); 67 | } 68 | 69 | &.disabled, 70 | &:disabled { 71 | color: $color; 72 | background-color: transparent; 73 | } 74 | 75 | &:not(:disabled):not(.disabled):active, 76 | &:not(:disabled):not(.disabled).active, 77 | .show>&.dropdown-toggle { 78 | color: color-yiq($active-background); 79 | background-color: $active-background; 80 | border-color: $active-border; 81 | 82 | &:focus { 83 | // Avoid using mixin so we can pass custom focus shadow properly 84 | @if $enable-shadows and $btn-active-box-shadow !=none { 85 | box-shadow: $btn-active-box-shadow, 0 0 0 $btn-focus-width rgba($color, .5); 86 | } 87 | @else { 88 | box-shadow: 0 0 0 $btn-focus-width rgba($color, .5); 89 | } 90 | } 91 | } 92 | } 93 | 94 | // Button sizes 95 | @mixin button-size($padding-y, $padding-x, $font-size, $line-height, $border-radius) { 96 | padding: $padding-y $padding-x; 97 | font-size: $font-size; 98 | line-height: $line-height; // Manually declare to provide an override to the browser default 99 | @if $enable-rounded { 100 | border-radius: $border-radius; 101 | } 102 | @else { 103 | border-radius: 0; 104 | } 105 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_forms.scss: -------------------------------------------------------------------------------- 1 | @mixin form-control-focus($ignore-warning) { 2 | &:focus { 3 | color: $input-focus-color; 4 | background-color: $input-focus-bg; 5 | border-color: $input-focus-border-color; 6 | outline: 0; 7 | // Avoid using mixin so we can pass custom focus shadow properly 8 | @if $enable-shadows { 9 | box-shadow: $input-box-shadow, $input-focus-box-shadow; 10 | } @else { 11 | box-shadow: $input-focus-box-shadow; 12 | } 13 | } 14 | } 15 | 16 | 17 | @mixin form-validation-state($state, $color, $icon) { 18 | .#{$state}-feedback { 19 | display: none; 20 | width: 100%; 21 | margin-top: $form-feedback-margin-top; 22 | font-size: $form-feedback-font-size; 23 | color: $color; 24 | } 25 | 26 | .#{$state}-tooltip { 27 | position: absolute; 28 | top: 100%; 29 | z-index: 5; 30 | display: none; 31 | max-width: 100%; // Contain to parent when possible 32 | padding: .5rem; 33 | margin-top: .1rem; 34 | font-size: .875rem; 35 | line-height: 1; 36 | color: #fff; 37 | background-color: rgba($color, .8); 38 | border-radius: .2rem; 39 | } 40 | 41 | .form-control, 42 | .custom-select { 43 | .was-validated &:#{$state}, 44 | &.is-#{$state} { 45 | border-color: $color; 46 | 47 | &:focus { 48 | border-color: $color; 49 | //box-shadow: 0 1px $input-focus-width 0 rgba($color, .75); 50 | } 51 | 52 | ~ .#{$state}-feedback, 53 | ~ .#{$state}-tooltip { 54 | display: block; 55 | } 56 | } 57 | } 58 | 59 | .form-check-input { 60 | .was-validated &:#{$state}, 61 | &.is-#{$state} { 62 | ~ .form-check-label { 63 | color: $color; 64 | } 65 | 66 | ~ .#{$state}-feedback, 67 | ~ .#{$state}-tooltip { 68 | display: block; 69 | } 70 | } 71 | } 72 | 73 | .custom-control-input { 74 | .was-validated &:#{$state}, 75 | &.is-#{$state} { 76 | ~ .custom-control-label { 77 | color: $color; 78 | 79 | &::before { 80 | background-color: lighten($color, 25%); 81 | border-color: lighten($color, 25%); 82 | } 83 | } 84 | 85 | ~ .#{$state}-feedback, 86 | ~ .#{$state}-tooltip { 87 | display: block; 88 | } 89 | 90 | &:checked { 91 | ~ .custom-control-label::before { 92 | @include gradient-bg(lighten($color, 10%)); 93 | border-color: lighten($color, 25%); 94 | } 95 | } 96 | 97 | &:focus { 98 | ~ .custom-control-label::before { 99 | box-shadow: 0 0 0 1px $body-bg, 0 0 0 $input-focus-width rgba($color, .25); 100 | } 101 | } 102 | } 103 | } 104 | 105 | // custom file 106 | .custom-file-input { 107 | .was-validated &:#{$state}, 108 | &.is-#{$state} { 109 | ~ .custom-file-label { 110 | border-color: $color; 111 | 112 | &::before { border-color: inherit; } 113 | } 114 | 115 | ~ .#{$state}-feedback, 116 | ~ .#{$state}-tooltip { 117 | display: block; 118 | } 119 | 120 | &:focus { 121 | ~ .custom-file-label { 122 | box-shadow: 0 0 0 $input-focus-width rgba($color, .25); 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_icon.scss: -------------------------------------------------------------------------------- 1 | @mixin icon-shape-variant($color) { 2 | color: saturate(darken($color, 10%), 10); 3 | background-color: transparentize(lighten($color, 10%), .5); 4 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_modals.scss: -------------------------------------------------------------------------------- 1 | @mixin modal-variant($background) { 2 | .modal-title { 3 | color: color-yiq($background); 4 | } 5 | .modal-header, 6 | .modal-footer { 7 | border-color: rgba(color-yiq($background), .075); 8 | } 9 | .modal-content { 10 | background-color: $background; 11 | color: color-yiq($background); 12 | 13 | .heading { 14 | color: color-yiq($background); 15 | } 16 | } 17 | 18 | .close { 19 | &>span:not(.sr-only) { 20 | color: $white; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/mixins/_popover.scss: -------------------------------------------------------------------------------- 1 | @mixin popover-variant($background) { 2 | 3 | background-color: $background; 4 | 5 | .popover-header { 6 | background-color: $background; 7 | color: color-yiq($background); 8 | } 9 | 10 | .popover-body { 11 | color: color-yiq($background); 12 | } 13 | .popover-header{ 14 | border-color: rgba(color-yiq($background), .2); 15 | } 16 | &.bs-popover-top { 17 | .arrow::after { 18 | border-top-color: $background; 19 | } 20 | } 21 | &.bs-popover-right { 22 | .arrow::after { 23 | border-right-color: $background; 24 | } 25 | } 26 | &.bs-popover-bottom { 27 | .arrow::after { 28 | border-bottom-color: $background; 29 | } 30 | } 31 | &.bs-popover-left { 32 | .arrow::after { 33 | border-left-color: $background; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/modals/_modal.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Modal 3 | // 4 | 5 | 6 | // Fluid modal 7 | 8 | .modal-fluid { 9 | .modal-dialog { 10 | margin-top: 0; 11 | margin-bottom: 0; 12 | } 13 | .modal-content { 14 | border-radius: 0; 15 | } 16 | } 17 | 18 | 19 | // Background color variations 20 | 21 | @each $color, $value in $theme-colors { 22 | .modal-#{$color} { 23 | @include modal-variant($value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navbars/_navbar-collapse.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Navabar collapse 3 | // 4 | 5 | // Collapse 6 | 7 | .navbar-collapse-header { 8 | display: none; 9 | } 10 | 11 | @include media-breakpoint-down(sm) { 12 | .navbar-nav { 13 | .nav-link { 14 | padding: .625rem 0; 15 | color: theme-color("default") !important; 16 | } 17 | 18 | .dropdown-menu { 19 | box-shadow: none; 20 | min-width: auto; 21 | 22 | .media { 23 | svg { 24 | width: 30px; 25 | } 26 | } 27 | } 28 | } 29 | 30 | .navbar-collapse { 31 | width: calc(100% - 1.4rem); 32 | position: absolute; 33 | top: 0; 34 | left: 0; 35 | right: 0; 36 | z-index: 1050; 37 | margin: .7rem; 38 | overflow-y: auto; 39 | height: auto !important; 40 | opacity: 0; 41 | 42 | .navbar-toggler { 43 | width: 20px; 44 | height: 20px; 45 | position: relative; 46 | cursor: pointer; 47 | display: inline-block; 48 | padding: 0; 49 | 50 | span { 51 | display: block; 52 | position: absolute; 53 | width: 100%; 54 | height: 2px; 55 | border-radius: 2px; 56 | opacity: 1; 57 | background: #283448; 58 | } 59 | 60 | :nth-child(1) { 61 | transform: rotate(135deg); 62 | } 63 | 64 | :nth-child(2) { 65 | transform: rotate(-135deg); 66 | } 67 | } 68 | 69 | .navbar-collapse-header { 70 | display: block; 71 | padding-bottom: 1rem; 72 | margin-bottom: 1rem; 73 | border-bottom: 1px solid rgba(0, 0, 0, .1); 74 | } 75 | 76 | .collapse-brand { 77 | img { 78 | height: 36px; 79 | } 80 | } 81 | 82 | .collapse-close { 83 | text-align: right; 84 | } 85 | } 86 | 87 | .navbar-collapse.collapsing, 88 | .navbar-collapse.show { 89 | padding: 1.5rem; 90 | border-radius: $border-radius; 91 | background: #FFF; 92 | box-shadow: 0 50px 100px rgba(50,50,93,.1),0 15px 35px rgba(50,50,93,.15),0 5px 15px rgba(0,0,0,.1); 93 | animation: show-navbar-collapse .2s ease forwards; 94 | } 95 | 96 | .navbar-collapse.collapsing-out { 97 | animation: hide-navbar-collapse .2s ease forwards; 98 | } 99 | } 100 | 101 | @keyframes show-navbar-collapse { 102 | 0% { 103 | opacity: 0; 104 | transform: scale(.95); 105 | transform-origin: 100% 0; 106 | } 107 | 108 | 100% { 109 | opacity: 1; 110 | transform: scale(1); 111 | } 112 | } 113 | 114 | @keyframes hide-navbar-collapse { 115 | from { 116 | opacity: 1; 117 | transform: scale(1); 118 | transform-origin: 100% 0; 119 | } 120 | 121 | to { 122 | opacity: 0; 123 | transform: scale(.95); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navbars/_navbar-dropdown.scss: -------------------------------------------------------------------------------- 1 | // Dropdown menu 2 | 3 | @include media-breakpoint-up(md) { 4 | .navbar { 5 | .dropdown-menu { 6 | opacity: 0; 7 | pointer-events: none; 8 | margin: 0; 9 | } 10 | 11 | .dropdown-menu-arrow { 12 | &:before { 13 | background: $dropdown-bg; 14 | box-shadow: none; 15 | content: ''; 16 | display: block; 17 | height: 12px; 18 | width: 12px; 19 | left: 20px; 20 | position: absolute; 21 | bottom: 100%; 22 | transform: rotate(-45deg) translateY(12px); 23 | z-index: -5; 24 | border-radius: 2px; 25 | } 26 | } 27 | 28 | .dropdown-menu-right { 29 | &:before { 30 | right: 20px; 31 | left: auto; 32 | } 33 | } 34 | 35 | &:not(.navbar-nav-hover) { 36 | .dropdown-menu { 37 | &.show { 38 | opacity: 1; 39 | pointer-events: auto; 40 | animation: show-navbar-dropdown .25s ease forwards; 41 | } 42 | 43 | &.close { 44 | display: block; 45 | animation: hide-navbar-dropdown .15s ease backwards; 46 | } 47 | } 48 | } 49 | 50 | &.navbar-nav-hover { 51 | .dropdown-menu { 52 | opacity: 0; 53 | display: block; 54 | pointer-events: none; 55 | transform: translate(0, 10px) perspective(200px) rotateX(-2deg); 56 | transition: visibility 0.25s, opacity 0.25s, transform 0.25s; 57 | } 58 | 59 | .nav-item.dropdown:hover > .dropdown-menu { 60 | display: block; 61 | opacity: 1; 62 | pointer-events: auto; 63 | visibility: visible; 64 | transform: translate(0, 0); 65 | animation: none; 66 | } 67 | } 68 | 69 | .dropdown-menu-inner { 70 | position: relative; 71 | padding: 1rem; 72 | } 73 | 74 | 75 | // Keyframes 76 | 77 | @keyframes show-navbar-dropdown { 78 | 0% { 79 | opacity: 0; 80 | transform: translate(0, 10px) perspective(200px) rotateX(-2deg); 81 | transition: visibility 0.25s, opacity 0.25s, transform 0.25s; 82 | } 83 | 84 | 100% { 85 | transform: translate(0, 0); 86 | opacity: 1; 87 | } 88 | } 89 | 90 | @keyframes hide-navbar-dropdown { 91 | from { 92 | opacity: 1; 93 | } 94 | 95 | to { 96 | opacity: 0; 97 | transform: translate(0, 10px); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navbars/_navbar-search.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Navbar search 3 | // 4 | 5 | .navbar-search { 6 | .input-group { 7 | border-radius: $navbar-search-border-radius; 8 | border: $navbar-search-border-width solid; 9 | background-color: transparent; 10 | 11 | .input-group-text { 12 | background-color: transparent; 13 | padding-left: 1rem; 14 | } 15 | } 16 | 17 | .form-control { 18 | width: $navbar-search-width; 19 | background-color: transparent; 20 | } 21 | } 22 | 23 | .navbar-search-dark { 24 | .input-group { 25 | border-color: $navbar-search-dark-border-color; 26 | } 27 | 28 | .input-group-text { 29 | color: $navbar-search-dark-color; 30 | } 31 | 32 | .form-control { 33 | color: $navbar-search-dark-focus-color; 34 | 35 | &::placeholder { 36 | color: $navbar-search-dark-color; 37 | } 38 | } 39 | 40 | .focused { 41 | .input-group { 42 | border-color: $navbar-search-dark-focus-border-color; 43 | } 44 | } 45 | } 46 | 47 | .navbar-search-light { 48 | .input-group { 49 | border-color: $navbar-search-light-border-color; 50 | } 51 | 52 | .input-group-text { 53 | color: $navbar-search-light-color; 54 | } 55 | 56 | .form-control { 57 | color: $navbar-search-light-focus-color; 58 | 59 | &::placeholder { 60 | color: $navbar-search-light-color; 61 | } 62 | } 63 | 64 | .focused { 65 | .input-group { 66 | border-color: $navbar-search-light-focus-border-color; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navbars/_navbar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Navbar 3 | // 4 | 5 | 6 | // Navbar links 7 | 8 | .navbar-horizontal { 9 | .navbar-nav { 10 | .nav-link { 11 | font-size: $navbar-nav-link-font-size; 12 | font-weight: $navbar-nav-link-font-weight; 13 | text-transform: $navbar-nav-link-text-transform; 14 | letter-spacing: $navbar-nav-link-letter-spacing; 15 | @include transition($navbar-transition); 16 | 17 | .nav-link-inner--text { 18 | margin-left: .25rem; 19 | } 20 | } 21 | } 22 | 23 | 24 | // Navbar brand (logo) 25 | 26 | .navbar-brand { 27 | font-size: $font-size-sm; 28 | font-weight: 600; 29 | text-transform: uppercase; 30 | font-size: .875rem; 31 | letter-spacing: .05px; 32 | 33 | img { 34 | height: 30px; 35 | } 36 | } 37 | 38 | .navbar-dark { 39 | .navbar-brand { 40 | color: $white; 41 | } 42 | } 43 | 44 | .navbar-light { 45 | .navbar-brand { 46 | color: $gray-800; 47 | } 48 | } 49 | 50 | .navbar-nav { 51 | .nav-item { 52 | .media:not(:last-child){ 53 | margin-bottom: 1.5rem; 54 | } 55 | } 56 | } 57 | 58 | @include media-breakpoint-up(lg) { 59 | .navbar-nav { 60 | .nav-item { 61 | margin-right: .5rem; 62 | 63 | [data-toggle="dropdown"]::after { 64 | transition: $transition-base; 65 | } 66 | 67 | &.show { 68 | [data-toggle="dropdown"]::after { 69 | transform: rotate(180deg); 70 | } 71 | } 72 | } 73 | .nav-link { 74 | padding-top: $navbar-nav-link-padding-y; 75 | padding-bottom: $navbar-nav-link-padding-y; 76 | border-radius: $navbar-nav-link-border-radius; 77 | 78 | i { 79 | margin-right: .625rem; 80 | } 81 | } 82 | 83 | .nav-link-icon { 84 | padding-left: .5rem !important; 85 | padding-right: .5rem !important; 86 | font-size: 1rem; 87 | border-radius: $navbar-nav-link-border-radius; 88 | 89 | i { 90 | margin-right: 0; 91 | } 92 | } 93 | } 94 | } 95 | 96 | 97 | // Transparent navbar 98 | 99 | .navbar-transparent { 100 | position: absolute; 101 | top: 0; 102 | width: 100%; 103 | z-index: 100; 104 | background-color: transparent; 105 | border: 0; 106 | box-shadow: none; 107 | 108 | .navbar-brand { 109 | color: rgba(255, 255, 255, 1); 110 | } 111 | 112 | .navbar-toggler { 113 | color: rgba(255, 255, 255, 1); 114 | } 115 | 116 | .navbar-toggler-icon { 117 | background-image: $navbar-dark-toggler-icon-bg; 118 | } 119 | } 120 | 121 | @include media-breakpoint-up(md) { 122 | .navbar-transparent { 123 | .navbar-nav { 124 | .nav-link { 125 | color: $navbar-dark-color; 126 | 127 | @include hover-focus { 128 | color: $navbar-dark-hover-color; 129 | } 130 | 131 | &.disabled { 132 | color: $navbar-dark-disabled-color; 133 | } 134 | } 135 | 136 | .show > .nav-link, 137 | .active > .nav-link, 138 | .nav-link.show, 139 | .nav-link.active { 140 | color: $navbar-dark-active-color; 141 | } 142 | } 143 | 144 | .navbar-brand { 145 | color: $navbar-dark-color; 146 | 147 | @include hover-focus { 148 | color: $navbar-dark-color; 149 | } 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navs/_nav-pills.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Nav pills 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .nav-pills { 9 | .nav-item:not(:last-child) { 10 | padding-right: $nav-pills-space-x; 11 | } 12 | 13 | .nav-link { 14 | padding: $nav-pills-padding-y $nav-pills-padding-x; 15 | color: $nav-pills-link-color; 16 | font-weight: 500; 17 | font-size: $font-size-sm; 18 | box-shadow: $nav-pills-box-shadow; 19 | background-color: $nav-pills-bg; 20 | transition: $transition-base; 21 | 22 | &:hover { 23 | color: $nav-pills-link-hover-color; 24 | } 25 | } 26 | 27 | .nav-link.active, 28 | .show > .nav-link { 29 | color: $nav-pills-link-active-color; 30 | background-color: $nav-pills-link-active-bg; 31 | } 32 | 33 | @include media-breakpoint-down(xs) { 34 | .nav-item { 35 | margin-bottom: $spacer; 36 | } 37 | } 38 | } 39 | 40 | @include media-breakpoint-down(sm) { 41 | .nav-pills:not(.nav-pills-circle) { 42 | .nav-item { 43 | padding-right: 0; 44 | } 45 | } 46 | } 47 | 48 | 49 | // Rounded circle nav pills 50 | 51 | .nav-pills-circle { 52 | .nav-link { 53 | text-align: center; 54 | height: 60px; 55 | width: 60px; 56 | padding: 0; 57 | line-height: 60px; 58 | border-radius: 50%; 59 | } 60 | 61 | .nav-link-icon { 62 | i, svg { 63 | font-size: 1rem; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/navs/_nav.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Nav 3 | // 4 | 5 | 6 | // Nav wrapper (container) 7 | 8 | // Nav wrapper 9 | .nav-wrapper { 10 | padding: 1rem 0; 11 | @include border-top-radius($card-border-radius); 12 | 13 | + .card { 14 | @include border-top-radius(0); 15 | @include border-bottom-radius($card-border-radius); 16 | } 17 | } 18 | 19 | 20 | // Nav links 21 | 22 | .nav-link { 23 | color: $nav-link-color; 24 | 25 | &:hover { 26 | color: $nav-link-hover-color; 27 | } 28 | 29 | i.ni { 30 | position: relative; 31 | top: 2px; 32 | } 33 | } 34 | 35 | @media (min-width: 992px){ 36 | .nav-item.active-pro { 37 | position: absolute; 38 | width: 100%; 39 | bottom: 10px; 40 | background: $table-head-bg; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/paginations/_pagination.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination 3 | // 4 | 5 | 6 | .page-item { 7 | &.active .page-link { 8 | box-shadow: $pagination-active-box-shadow; 9 | } 10 | 11 | .page-link, 12 | span { 13 | display: flex; 14 | align-items: center; 15 | justify-content: center; 16 | padding: 0; 17 | margin: 0 3px; 18 | border-radius: 50% !important; 19 | width: 36px; 20 | height: 36px; 21 | font-size: $font-size-sm; 22 | } 23 | } 24 | 25 | 26 | // Size variations 27 | 28 | .pagination-lg { 29 | .page-item { 30 | .page-link, 31 | span { 32 | width: 46px; 33 | height: 46px; 34 | line-height: 46px; 35 | } 36 | } 37 | } 38 | 39 | .pagination-sm { 40 | .page-item { 41 | .page-link, 42 | span { 43 | width: 30px; 44 | height: 30px; 45 | line-height: 30px; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/popovers/_popover.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Popover 3 | // 4 | 5 | 6 | .popover { 7 | border: 0; 8 | } 9 | 10 | .popover-header { 11 | font-weight: $font-weight-bold; 12 | } 13 | 14 | 15 | // Alternative colors 16 | 17 | @each $color, $value in $theme-colors { 18 | .popover-#{$color} { 19 | @include popover-variant($value); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/progresses/_progress.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Progress 3 | // 4 | 5 | 6 | // Progress container 7 | 8 | .progress-wrapper { 9 | position: relative; 10 | padding-top: 1.5rem; 11 | } 12 | 13 | 14 | // General styles 15 | 16 | .progress { 17 | height: 8px; 18 | margin-bottom: $spacer; 19 | overflow: hidden; 20 | border-radius: $border-radius-sm; 21 | background-color: $progress-bg; 22 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 23 | 24 | .sr-only { 25 | width: auto; 26 | height: 20px; 27 | margin: 0 0 0 30px; 28 | left: 0; 29 | clip: auto; 30 | line-height: 20px; 31 | font-size: 13px; 32 | } 33 | } 34 | 35 | 36 | // Progress inner elements 37 | 38 | .progress-heading { 39 | font-size: 14px; 40 | font-weight: 500; 41 | margin: 0 0 2px; 42 | padding: 0; 43 | } 44 | 45 | .progress-bar { 46 | box-shadow: none; 47 | border-radius: 0; 48 | height: auto; 49 | } 50 | 51 | .progress-info{ 52 | margin-bottom: .5rem; 53 | display: flex; 54 | align-items: center; 55 | justify-content: space-between; 56 | } 57 | 58 | .progress-label { 59 | span { 60 | display: inline-block; 61 | color: $primary; 62 | font-size: .625rem; 63 | font-weight: 600; 64 | text-transform: uppercase; 65 | background: rgba($primary, .1); 66 | padding: .25rem 1rem; 67 | border-radius: 30px; 68 | } 69 | } 70 | 71 | .progress-percentage { 72 | text-align: right; 73 | span { 74 | display: inline-block; 75 | color: $gray-600; 76 | font-size: .875rem; 77 | font-weight: 600; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/separators/_separator.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Separator 3 | // add svg on top or bottom of a section for a more stylish visual 4 | // 5 | 6 | 7 | .separator { 8 | position: absolute; 9 | top: auto; 10 | left: 0; 11 | right: 0; 12 | width: 100%; 13 | height: 150px; 14 | transform: translateZ(0); 15 | overflow: hidden; 16 | pointer-events: none; 17 | 18 | svg { 19 | position: absolute; 20 | pointer-events: none; 21 | } 22 | } 23 | 24 | .separator-top { 25 | top: 0; 26 | bottom: auto; 27 | 28 | svg { 29 | top: 0; 30 | } 31 | } 32 | 33 | .separator-bottom { 34 | top: auto; 35 | bottom: 0; 36 | 37 | svg { 38 | bottom: 0; 39 | } 40 | } 41 | 42 | .separator-inverse { 43 | transform: rotate(180deg); 44 | } 45 | 46 | // Styles 47 | 48 | .separator-skew { 49 | height: 60px; 50 | 51 | @include media-breakpoint-up(xl) { 52 | height: 70px; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/tables/_table.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Table 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .table { 9 | thead th { 10 | padding-top: $table-head-spacer-y; 11 | padding-bottom: $table-head-spacer-y; 12 | font-size: $table-head-font-size; 13 | text-transform: $table-head-text-transform; 14 | letter-spacing: $table-head-letter-spacing; 15 | border-bottom: $table-border-width solid $table-border-color; 16 | } 17 | 18 | th { 19 | font-weight: $table-head-font-weight; 20 | } 21 | 22 | td { 23 | .progress { 24 | height: 3px; 25 | width: 120px; 26 | margin: 0; 27 | } 28 | } 29 | 30 | td, 31 | th { 32 | font-size: $table-body-font-size; 33 | white-space: nowrap; 34 | } 35 | 36 | 37 | // Vetical align table content 38 | 39 | &.align-items-center { 40 | td, 41 | th { 42 | vertical-align: middle; 43 | } 44 | } 45 | 46 | 47 | // Styles for dark table 48 | 49 | .thead-dark { 50 | th { 51 | background-color: $table-dark-head-bg; 52 | color: $table-dark-head-color; 53 | } 54 | } 55 | 56 | 57 | // Styles for light table 58 | 59 | .thead-light { 60 | th { 61 | background-color: $table-head-bg; 62 | color: $table-head-color; 63 | } 64 | } 65 | } 66 | 67 | 68 | // Add transition for hover state 69 | 70 | .table-hover { 71 | tr { 72 | @include transition($transition-base); 73 | } 74 | } 75 | 76 | 77 | // Flush tables 78 | 79 | .table-flush { 80 | td, 81 | th { 82 | border-left: 0; 83 | border-right: 0; 84 | } 85 | 86 | tbody { 87 | tr { 88 | &:first-child { 89 | td, 90 | th { 91 | border-top: 0; 92 | } 93 | } 94 | 95 | &:last-child { 96 | td, 97 | th { 98 | border-bottom: 0; 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | 106 | // Tables inside cards 107 | 108 | .card { 109 | .table { 110 | margin-bottom: 0; 111 | 112 | td, 113 | th { 114 | padding-left: $card-spacer-x; 115 | padding-right: $card-spacer-x; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/type/_article.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Article 3 | // 4 | 5 | article { 6 | h4:not(:first-child), 7 | h5:not(:first-child) { 8 | margin-top: 3rem; 9 | } 10 | 11 | h4, h5 { 12 | margin-bottom: 1.5rem; 13 | } 14 | 15 | figure { 16 | margin: 3rem 0; 17 | } 18 | 19 | h5 + figure { 20 | margin-top: 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/type/_display.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Display 3 | // 4 | 5 | 6 | .display-1, 7 | .display-2, 8 | .display-3, 9 | .display-4 { 10 | span { 11 | display: block; 12 | font-weight: $font-weight-light; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/type/_heading.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Heading 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .heading { 9 | letter-spacing: $heading-letter-spacing; 10 | font-size: $heading-font-size; 11 | text-transform: $heading-text-transform; 12 | font-weight: $heading-font-weight; 13 | } 14 | 15 | 16 | // Heading variations 17 | 18 | .heading-small { 19 | padding-top: .25rem; 20 | padding-bottom: .25rem; 21 | font-size: .75rem; 22 | text-transform: uppercase; 23 | letter-spacing: .04em; 24 | } 25 | 26 | .heading-title { 27 | letter-spacing: $heading-title-letter-spacing; 28 | font-size: $heading-title-font-size; 29 | font-weight: $heading-title-font-weight; 30 | text-transform: $heading-title-text-transform; 31 | } 32 | 33 | .heading-section { 34 | letter-spacing: $heading-section-letter-spacing; 35 | font-size: $heading-section-font-size; 36 | font-weight: $heading-section-font-weight; 37 | text-transform: $heading-section-text-transform; 38 | 39 | img { 40 | display: block; 41 | width: 72px; 42 | height: 72px; 43 | margin-bottom: 1.5rem; 44 | } 45 | 46 | &.text-center { 47 | img { 48 | margin-left: auto; 49 | margin-right: auto; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/type/_type.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Type 3 | // 4 | 5 | 6 | // Paragraphs 7 | 8 | p { 9 | font-size: $paragraph-font-size; 10 | font-weight: $paragraph-font-weight; 11 | line-height: $paragraph-line-height; 12 | } 13 | 14 | .lead { 15 | font-size: $lead-font-size; 16 | font-weight: $lead-font-weight; 17 | line-height: $paragraph-line-height; 18 | margin-top: 1.5rem; 19 | 20 | + .btn-wrapper { 21 | margin-top: 3rem; 22 | } 23 | } 24 | 25 | .description { 26 | font-size: $font-size-sm; 27 | } 28 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_backgrounds.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Backgrounds 3 | // 4 | 5 | 6 | @each $color, $value in $colors { 7 | @include bg-variant(".bg-#{$color}", $value); 8 | } 9 | 10 | @each $color, $value in $theme-colors { 11 | @include bg-gradient-variant(".bg-gradient-#{$color}", $value); 12 | } 13 | 14 | @each $color, $value in $colors { 15 | @include bg-gradient-variant(".bg-gradient-#{$color}", $value); 16 | } 17 | 18 | 19 | // Background colors with transparency 20 | 21 | @each $color, $value in $theme-colors { 22 | @include bg-translucent-variant(".bg-translucent-#{$color}", $value); 23 | } 24 | 25 | 26 | // Sections backgrounds 27 | 28 | @each $color, $value in $section-colors { 29 | @include bg-variant(".section-#{$color}", $value); 30 | } 31 | 32 | @each $color, $value in $theme-colors { 33 | @include bg-gradient-variant(".bg-gradient-#{$color}", $value); 34 | } 35 | 36 | 37 | // Shape (svg) fill colors 38 | 39 | @each $color, $value in $theme-colors { 40 | .fill-#{$color} { 41 | fill: $value; 42 | } 43 | 44 | .stroke-#{$color} { 45 | stroke: $value; 46 | } 47 | } 48 | 49 | .fill-opacity-8 { 50 | fill-opacity: .8; 51 | } 52 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_blurable.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Blurable 3 | // add a blue effect on hover on any element with .blur--hover class 4 | // 5 | 6 | .blur--hover { 7 | position: relative; 8 | 9 | .blur-item { 10 | transition: 1s cubic-bezier(.19,1,.22,1); 11 | will-change: transform; 12 | filter: blur(0); 13 | opacity: 1; 14 | } 15 | 16 | .blur-hidden { 17 | position: absolute; 18 | top: calc(50% + 7px); 19 | left: 50%; 20 | transform: translate(-50%, -50%); 21 | opacity: 0; 22 | transition: $transition-base; 23 | z-index: 100; 24 | } 25 | &:hover { 26 | .blur-item { 27 | opacity: .8; 28 | filter: blur(10px); 29 | transform: scale(.95); 30 | z-index: 1; 31 | } 32 | .blur-hidden { 33 | opacity: 1; 34 | top: 50%; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_floating.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Floating 3 | // floating animation utility class 4 | // 5 | 6 | 7 | .floating { 8 | animation: floating 3s ease infinite; 9 | will-change: transform; 10 | 11 | &:hover { 12 | animation-play-state: paused; 13 | } 14 | } 15 | 16 | 17 | // Size variations 18 | 19 | .floating-lg { 20 | animation: floating-lg 3s ease infinite; 21 | } 22 | 23 | .floating-sm { 24 | animation: floating-sm 3s ease infinite; 25 | } 26 | 27 | 28 | // Keyframes 29 | 30 | @keyframes floating-lg { 31 | 0% { 32 | transform: translateY(0px) 33 | } 34 | 50% { 35 | transform: translateY(15px) 36 | } 37 | 100% { 38 | transform: translateY(0px) 39 | } 40 | } 41 | 42 | @keyframes floating { 43 | 0% { 44 | transform: translateY(0px) 45 | } 46 | 50% { 47 | transform: translateY(10px) 48 | } 49 | 100% { 50 | transform: translateY(0px) 51 | } 52 | } 53 | 54 | @keyframes floating-sm { 55 | 0% { 56 | transform: translateY(0px) 57 | } 58 | 50% { 59 | transform: translateY(5px) 60 | } 61 | 100% { 62 | transform: translateY(0px) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_helper.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Helper 3 | // helper classes for different cases 4 | // 5 | 6 | 7 | // Clearfix for sections that use float property 8 | 9 | .floatfix { 10 | &:before, 11 | &:after { 12 | content: ''; 13 | display: table; 14 | } 15 | &:after { 16 | clear: both; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_image.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Image 3 | // 4 | 5 | .img-center { 6 | display: block; 7 | margin-left: auto; 8 | margin-right: auto; 9 | } 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_opacity.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Opacity 3 | // modify the transparency of an element with this quick modifier classes 4 | // 5 | 6 | .opacity-1 { 7 | opacity: .1 !important; 8 | } 9 | .opacity-2 { 10 | opacity: .2 !important; 11 | } 12 | .opacity-3 { 13 | opacity: .3 !important; 14 | } 15 | .opacity-4 { 16 | opacity: .4 !important; 17 | } 18 | .opacity-5 { 19 | opacity: .5 !important; 20 | } 21 | .opacity-6 { 22 | opacity: .6 !important; 23 | } 24 | .opacity-7 { 25 | opacity: .7 !important; 26 | } 27 | .opacity-8 { 28 | opacity: .8 !important; 29 | } 30 | .opacity-8 { 31 | opacity: .9 !important; 32 | } 33 | .opacity-10 { 34 | opacity: 1 !important; 35 | } 36 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_overflow.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Overflow 3 | // 4 | 5 | .overflow-visible { 6 | overflow: visible !important; 7 | } 8 | 9 | .overflow-hidden { 10 | overflow: hidden !important; 11 | } 12 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_position.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Position 3 | // modifier classes to be applied on an abosolute positioned element 4 | // use it next to .position-absolute class 5 | // 6 | 7 | @each $size, $value in $spacers { 8 | .top-#{$size} { 9 | top: $value; 10 | } 11 | .right-#{$size} { 12 | right: $value; 13 | } 14 | .bottom-#{$size} { 15 | bottom: $value; 16 | } 17 | .left-#{$size} { 18 | left: $value; 19 | } 20 | } 21 | 22 | .center { 23 | left: 50%; 24 | transform: translateX(-50%); 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_shadows.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Shadows 3 | // 4 | 5 | // General styles 6 | [class*="shadow"] { 7 | @if $enable-transitions { 8 | transition: $transition-base; 9 | } 10 | } 11 | 12 | 13 | // Size variations 14 | .shadow-sm--hover:hover { 15 | box-shadow: $box-shadow-sm !important; 16 | } 17 | 18 | .shadow--hover:hover { 19 | box-shadow: $box-shadow !important; 20 | } 21 | 22 | .shadow-lg--hover:hover { 23 | box-shadow: $box-shadow-lg !important; 24 | } 25 | 26 | .shadow-none--hover:hover { 27 | box-shadow: none !important; 28 | } 29 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_sizing.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Height 3 | // 4 | 5 | .h-100vh { 6 | height: 100vh !important; 7 | } 8 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_spacing.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Spacing 3 | // 4 | 5 | .row.row-grid > [class*="col-"] + [class*="col-"] { 6 | margin-top: 3rem; 7 | } 8 | 9 | @include media-breakpoint-up(lg) { 10 | .row.row-grid > [class*="col-lg-"] + [class*="col-lg-"] { 11 | margin-top: 0; 12 | } 13 | } 14 | @include media-breakpoint-up(md) { 15 | .row.row-grid > [class*="col-md-"] + [class*="col-md-"] { 16 | margin-top: 0; 17 | } 18 | } 19 | @include media-breakpoint-up(sm) { 20 | .row.row-grid > [class*="col-sm-"] + [class*="col-sm-"] { 21 | margin-top: 0; 22 | } 23 | } 24 | 25 | .row-grid + .row-grid { 26 | margin-top: 3rem; 27 | } 28 | 29 | 30 | // Negative margins and paddings 31 | 32 | @media(min-width: 992px) { 33 | [class*="mt--"], 34 | [class*="mr--"], 35 | [class*="mb--"], 36 | [class*="ml--"] { 37 | 38 | } 39 | 40 | 41 | // Large negative margins in pixels 42 | 43 | .mt--100 { 44 | margin-top: -100px !important; 45 | } 46 | .mr--100 { 47 | margin-right: -100px !important; 48 | } 49 | .mb--100 { 50 | margin-bottom: -100px !important; 51 | } 52 | .ml--100 { 53 | margin-left: -100px !important; 54 | } 55 | .mt--150 { 56 | margin-top: -150px !important; 57 | } 58 | .mb--150 { 59 | margin-bottom: -150px !important; 60 | } 61 | .mt--200 { 62 | margin-top: -200px !important; 63 | } 64 | .mb--200 { 65 | margin-bottom: -200px !important; 66 | } 67 | .mt--300 { 68 | margin-top: -300px !important; 69 | } 70 | .mb--300 { 71 | margin-bottom: -300px !important; 72 | } 73 | 74 | 75 | // Large margins in pixels 76 | 77 | .pt-100 { 78 | padding-top: 100px !important; 79 | } 80 | .pb-100 { 81 | padding-bottom: 100px !important; 82 | } 83 | .pt-150 { 84 | padding-top: 150px !important; 85 | } 86 | .pb-150 { 87 | padding-bottom: 150px !important; 88 | } 89 | .pt-200 { 90 | padding-top: 200px !important; 91 | } 92 | .pb-200 { 93 | padding-bottom: 200px !important; 94 | } 95 | .pt-250 { 96 | padding-top: 250px !important; 97 | } 98 | .pb-250 { 99 | padding-bottom: 250px !important; 100 | } 101 | .pt-300 { 102 | padding-top: 300px!important; 103 | } 104 | .pb-300 { 105 | padding-bottom: 300px!important; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_text.scss: -------------------------------------------------------------------------------- 1 | // Weight and italics 2 | 3 | .font-weight-300 { font-weight: 300 !important; } 4 | .font-weight-400 { font-weight: 400 !important; } 5 | .font-weight-500 { font-weight: 500 !important; } 6 | .font-weight-600 { font-weight: 600 !important; } 7 | .font-weight-700 { font-weight: 700 !important; } 8 | .font-weight-800 { font-weight: 800 !important; } 9 | .font-weight-900 { font-weight: 900 !important; } 10 | 11 | 12 | // Text decorations 13 | 14 | .text-underline { text-decoration: underline; } 15 | .text-through { text-decoration: line-through; } 16 | 17 | 18 | // Text size 19 | 20 | .text-xs { font-size: $font-size-xs !important; } 21 | .text-sm { font-size: $font-size-sm !important; } 22 | .text-lg { font-size: $font-size-lg !important; } 23 | .text-xl { font-size: $font-size-xl !important; } 24 | 25 | 26 | // Line heights 27 | 28 | .lh-100 { line-height: 1; } 29 | .lh-110 { line-height: 1.1; } 30 | .lh-120 { line-height: 1.2; } 31 | .lh-130 { line-height: 1.3; } 32 | .lh-140 { line-height: 1.4; } 33 | .lh-150 { line-height: 1.5; } 34 | .lh-160 { line-height: 1.6; } 35 | .lh-170 { line-height: 1.7; } 36 | .lh-180 { line-height: 1.8; } 37 | 38 | 39 | // Letter spacings 40 | 41 | .ls-1 { letter-spacing: .0625rem; } 42 | .ls-15 { letter-spacing: .09375rem; } 43 | .ls-2 { letter-spacing: 0.125rem; } 44 | 45 | // Color variations 46 | 47 | @each $color, $value in $colors { 48 | @include text-emphasis-variant(".text-#{$color}", $value, $ignore-warning: true); 49 | } 50 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/utilities/_transform.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Tranform 3 | // 4 | 5 | 6 | @include media-breakpoint-up(lg) { 7 | .transform-perspective-right { 8 | transform: scale(1) perspective(1040px) rotateY(-11deg) rotateX(2deg) rotate(2deg); 9 | } 10 | .transform-perspective-left{ 11 | transform: scale(1) perspective(2000px) rotateY(11deg) rotateX(2deg) rotate(-2deg) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/vendors/_headroom.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Headroom 3 | // 4 | 5 | 6 | .headroom { 7 | will-change: transform; 8 | background-color: inherit; 9 | @include transition($transition-base); 10 | } 11 | .headroom--pinned { 12 | @extend .position-fixed; 13 | transform: translateY(0%); 14 | } 15 | .headroom--unpinned { 16 | @extend .position-fixed; 17 | transform: translateY(-100%); 18 | } 19 | 20 | .headroom--not-top { 21 | padding-top: .5rem; 22 | padding-bottom: .5rem; 23 | background-color: theme-color("default") !important; 24 | box-shadow: 0 1px 10px rgba(130, 130, 134, 0.1); 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/custom/vendors/_scrollbar.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Custom scrollbar 3 | // 4 | 5 | .scrollbar-inner { 6 | height: 100%; 7 | 8 | &:not(:hover) .scroll-element { 9 | opacity: 0; 10 | } 11 | 12 | .scroll-element { 13 | transition: opacity 300ms; 14 | margin-right: 2px; 15 | 16 | .scroll-bar, 17 | .scroll-element_track { 18 | transition: background-color 300ms; 19 | } 20 | 21 | .scroll-element_track { 22 | background-color: transparent; 23 | } 24 | } 25 | 26 | .scroll-element.scroll-y { 27 | width: 3px; 28 | right: 0; 29 | } 30 | 31 | .scroll-element.scroll-x { 32 | height: 3px; 33 | bottom: 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/docs/_clipboard-js.scss: -------------------------------------------------------------------------------- 1 | // clipboard.js 2 | // 3 | // JS-based `Copy` buttons for code snippets. 4 | 5 | .ct-clipboard { 6 | position: relative; 7 | display: none; 8 | float: right; 9 | 10 | + .highlight { 11 | margin-top: 0; 12 | } 13 | } 14 | 15 | .btn-clipboard { 16 | position: absolute; 17 | top: 1rem; 18 | right: 1rem; 19 | z-index: 10; 20 | display: block; 21 | padding: .25rem .5rem; 22 | font-size: 75%; 23 | cursor: pointer; 24 | background-color: transparent; 25 | border: 0; 26 | border-radius: .25rem; 27 | color: #fff; 28 | background-color: $ct-primary; 29 | 30 | &:hover { 31 | color: #fff; 32 | background-color: darken($ct-primary, 10%); 33 | } 34 | } 35 | 36 | @media (min-width: 768px) { 37 | .ct-clipboard { 38 | display: block; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/docs/_footer.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Footer 3 | // 4 | .ct-footer { 5 | font-size: 85%; 6 | text-align: center; 7 | background-color: #f7f7f7; 8 | 9 | a { 10 | font-weight: 500; 11 | color: $gray-700; 12 | 13 | &:hover, 14 | &:focus { 15 | color: $link-color; 16 | } 17 | } 18 | 19 | p { 20 | margin-bottom: 0; 21 | } 22 | 23 | @include media-breakpoint-up(sm) { 24 | text-align: left; 25 | } 26 | } 27 | 28 | .ct-footer-links { 29 | padding-left: 0; 30 | margin-bottom: 1rem; 31 | 32 | li { 33 | display: inline-block; 34 | 35 | +li { 36 | margin-left: 1rem; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/docs/_nav.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Main navbar 3 | // 4 | .ct-navbar { 5 | background-color: $ct-primary; 6 | box-shadow: rgba(116, 129, 141, 0.1) 0px 1px 1px 0px; 7 | padding-top: .5rem; 8 | padding-bottom: .5rem; 9 | 10 | @include media-breakpoint-down(md) { 11 | padding-right: .5rem; 12 | padding-left: .5rem; 13 | 14 | .navbar-nav-scroll { 15 | max-width: 100%; 16 | height: 2.5rem; 17 | margin-top: .25rem; 18 | overflow: hidden; 19 | font-size: .875rem; 20 | 21 | .navbar-nav { 22 | padding-bottom: 2rem; 23 | overflow-x: auto; 24 | white-space: nowrap; 25 | -webkit-overflow-scrolling: touch; 26 | } 27 | } 28 | } 29 | 30 | @include media-breakpoint-up(md) { 31 | @supports (position: sticky) { 32 | position: sticky; 33 | top: 0; 34 | z-index: 1071; // over everything in bootstrap 35 | } 36 | } 37 | 38 | .navbar-nav { 39 | .nav-link { 40 | padding-right: .5rem; 41 | padding-left: .5rem; 42 | color: $ct-primary-light !important; 43 | 44 | &.active, 45 | &:hover { 46 | color: #fff !important; 47 | background-color: transparent !important; 48 | } 49 | 50 | &.active { 51 | font-weight: 500; 52 | } 53 | } 54 | } 55 | 56 | .navbar-nav-svg { 57 | display: inline-block; 58 | width: 1rem; 59 | height: 1rem; 60 | vertical-align: text-top; 61 | } 62 | 63 | .dropdown-menu { 64 | font-size: .875rem; 65 | } 66 | 67 | .dropdown-item.active { 68 | font-weight: 500; 69 | color: $gray-900; 70 | background-color: transparent; 71 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23292b2c' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E"); 72 | background-repeat: no-repeat; 73 | background-position: .4rem .87rem; 74 | background-size: .75rem .75rem; 75 | padding-left: 25px; 76 | } 77 | } 78 | 79 | // Github corner 80 | .github-corner { 81 | position: fixed; 82 | right: 0; 83 | z-index: 1080; 84 | 85 | &:hover { 86 | .octo-arm { 87 | animation: octocat-wave 560ms ease-in-out 88 | } 89 | } 90 | 91 | svg { 92 | fill: $white; 93 | color: $ct-primary; 94 | } 95 | } 96 | 97 | @keyframes octocat-wave { 98 | 0%, 99 | 100% { 100 | transform: rotate(0) 101 | } 102 | 20%, 103 | 60% { 104 | transform: rotate(-25deg) 105 | } 106 | 40%, 107 | 80% { 108 | transform: rotate(10deg) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/docs/_prism.scss: -------------------------------------------------------------------------------- 1 | code[class*="language-"], 2 | pre[class*="language-"] { 3 | font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", Courier, monospace; 4 | font-size: 14px; 5 | line-height: 1.375; 6 | direction: ltr; 7 | text-align: left; 8 | white-space: pre; 9 | word-spacing: normal; 10 | word-break: normal; 11 | -moz-tab-size: 4; 12 | -o-tab-size: 4; 13 | tab-size: 4; 14 | -webkit-hyphens: none; 15 | -moz-hyphens: none; 16 | -ms-hyphens: none; 17 | hyphens: none; 18 | background: #f5f7ff; 19 | color: #5e6687; 20 | border-radius: .25rem; 21 | } 22 | 23 | pre[class*="language-"]::-moz-selection, 24 | pre[class*="language-"] ::-moz-selection, 25 | code[class*="language-"]::-moz-selection, 26 | code[class*="language-"] ::-moz-selection { 27 | text-shadow: none; 28 | background: #dfe2f1; 29 | } 30 | 31 | pre[class*="language-"]::selection, 32 | pre[class*="language-"] ::selection, 33 | code[class*="language-"]::selection, 34 | code[class*="language-"] ::selection { 35 | text-shadow: none; 36 | background: #dfe2f1; 37 | } 38 | 39 | 40 | /* Code blocks */ 41 | 42 | pre[class*="language-"] { 43 | padding: 1.25rem; 44 | margin: 0; 45 | overflow: auto; 46 | } 47 | 48 | 49 | /* Inline code */ 50 | 51 | :not(pre)>code[class*="language-"] { 52 | padding: .1em; 53 | border-radius: .3em; 54 | } 55 | 56 | .token.comment, 57 | .token.prolog, 58 | .token.doctype, 59 | .token.cdata { 60 | color: #898ea4; 61 | } 62 | 63 | .token.punctuation { 64 | color: #5e6687; 65 | } 66 | 67 | .token.namespace { 68 | opacity: .7; 69 | } 70 | 71 | .token.operator, 72 | .token.boolean, 73 | .token.number { 74 | color: #c76b29; 75 | } 76 | 77 | .token.property { 78 | color: #c08b30; 79 | } 80 | 81 | .token.tag { 82 | color: #3d8fd1; 83 | } 84 | 85 | .token.string { 86 | color: #22a2c9; 87 | } 88 | 89 | .token.selector { 90 | color: #6679cc; 91 | } 92 | 93 | .token.attr-name { 94 | color: #c76b29; 95 | } 96 | 97 | .token.entity, 98 | .token.url, 99 | .language-css .token.string, 100 | .style .token.string { 101 | color: #22a2c9; 102 | } 103 | 104 | .token.attr-value, 105 | .token.keyword, 106 | .token.control, 107 | .token.directive, 108 | .token.unit { 109 | color: #ac9739; 110 | } 111 | 112 | .token.statement, 113 | .token.regex, 114 | .token.atrule { 115 | color: #22a2c9; 116 | } 117 | 118 | .token.placeholder, 119 | .token.variable { 120 | color: #3d8fd1; 121 | } 122 | 123 | .token.deleted { 124 | text-decoration: line-through; 125 | } 126 | 127 | .token.inserted { 128 | border-bottom: 1px dotted #202746; 129 | text-decoration: none; 130 | } 131 | 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.important, 137 | .token.bold { 138 | font-weight: bold; 139 | } 140 | 141 | .token.important { 142 | color: #c94922; 143 | } 144 | 145 | .token.entity { 146 | cursor: help; 147 | } 148 | 149 | pre>code.highlight { 150 | outline: 0.4em solid #c94922; 151 | outline-offset: .4em; 152 | } 153 | 154 | 155 | /* overrides color-values for the Line Numbers plugin 156 | * http://prismjs.com/plugins/line-numbers/ 157 | */ 158 | 159 | .line-numbers .line-numbers-rows { 160 | border-right-color: #dfe2f1; 161 | } 162 | 163 | .line-numbers-rows>span:before { 164 | color: #979db4; 165 | } 166 | 167 | 168 | /* overrides color-values for the Line Highlight plugin 169 | * http://prismjs.com/plugins/line-highlight/ 170 | */ 171 | 172 | .line-highlight { 173 | background: rgba(107, 115, 148, 0.2); 174 | background: -webkit-linear-gradient(left, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); 175 | background: linear-gradient(to right, rgba(107, 115, 148, 0.2) 70%, rgba(107, 115, 148, 0)); 176 | } -------------------------------------------------------------------------------- /client/src/assets/scss/argon-dashboard/docs/_variables.scss: -------------------------------------------------------------------------------- 1 | // Local docs variables 2 | $ct-primary: theme-color("primary") !default; 3 | $ct-primary-bright: lighten(saturate($ct-primary, 5%), 15%) !default; 4 | $ct-primary-light: rgba(255, 255, 255, .9) !default; 5 | $ct-dark: #2a2730 !default; 6 | $ct-download: #ffe484 !default; 7 | $ct-info: #5bc0de !default; 8 | $ct-warning: #f0ad4e !default; 9 | $ct-danger: #d9534f !default; 10 | 11 | $ct-sidebar-bg: #f5f7f9; 12 | $ct-sidebar-border-color: #e6ecf1; 13 | 14 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btn{ 2 | .btn-inner--icon{ 3 | margin-right: 4px; 4 | } 5 | } 6 | .btn + .btn { 7 | margin-left: 4px; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/_mixins.scss: -------------------------------------------------------------------------------- 1 | button:focus, :focus { 2 | outline: none; 3 | } 4 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/_navbar-dropdown.scss: -------------------------------------------------------------------------------- 1 | @keyframes show-navbar-dropdown { 2 | 0% { 3 | opacity: 0; 4 | transition: visibility 0.25s, opacity 0.25s; 5 | } 6 | 100% { 7 | opacity: 1; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/_navbar.scss: -------------------------------------------------------------------------------- 1 | .navbar .collapsing .navbar-toggler{ 2 | pointer-events: none; 3 | } 4 | .navbar .nav-item .nav-link-icon.nav-link i{ 5 | margin-right: 4px; 6 | } 7 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/_tables.scss: -------------------------------------------------------------------------------- 1 | .table { 2 | .avatar-group .avatar 3 | { 4 | margin-right: 4px; 5 | } 6 | .badge i{ 7 | margin-right: 10px; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/bootstrap/_spinners.scss: -------------------------------------------------------------------------------- 1 | $spinner-width: 2rem !default; 2 | $spinner-height: $spinner-width !default; 3 | $spinner-border-width: .25em !default; 4 | 5 | $spinner-width-sm: 1rem !default; 6 | $spinner-height-sm: $spinner-width-sm !default; 7 | $spinner-border-width-sm: .2em !default; 8 | 9 | // 10 | // Rotating border 11 | // 12 | 13 | @keyframes spinner-border { 14 | to { transform: rotate(360deg); } 15 | } 16 | 17 | .spinner-border { 18 | display: inline-block; 19 | width: $spinner-width; 20 | height: $spinner-height; 21 | vertical-align: text-bottom; 22 | border: $spinner-border-width solid currentColor; 23 | border-right-color: transparent; 24 | // stylelint-disable-next-line property-blacklist 25 | border-radius: 50%; 26 | animation: spinner-border .75s linear infinite; 27 | } 28 | 29 | .spinner-border-sm { 30 | width: $spinner-width-sm; 31 | height: $spinner-height-sm; 32 | border-width: $spinner-border-width-sm; 33 | } 34 | 35 | // 36 | // Growing circle 37 | // 38 | 39 | @keyframes spinner-grow { 40 | 0% { 41 | transform: scale(0); 42 | } 43 | 50% { 44 | opacity: 1; 45 | } 46 | } 47 | 48 | .spinner-grow { 49 | display: inline-block; 50 | width: $spinner-width; 51 | height: $spinner-height; 52 | vertical-align: text-bottom; 53 | background-color: currentColor; 54 | // stylelint-disable-next-line property-blacklist 55 | border-radius: 50%; 56 | opacity: 0; 57 | animation: spinner-grow .75s linear infinite; 58 | } 59 | 60 | .spinner-grow-sm { 61 | width: $spinner-width-sm; 62 | height: $spinner-height-sm; 63 | } 64 | -------------------------------------------------------------------------------- /client/src/assets/scss/react/react-differences.scss: -------------------------------------------------------------------------------- 1 | // Differences from the HTML to the React product 2 | 3 | // bootstrap 4 | @import "~bootstrap/scss/spinners"; 5 | // react plugins 6 | @import "plugins/plugin-react-datetime"; 7 | // core components 8 | @import "buttons"; 9 | @import "mixins"; 10 | @import "navbar-dropdown"; 11 | @import "navbar"; 12 | @import "tables"; 13 | -------------------------------------------------------------------------------- /client/src/components/Charts/TicketsPieChart.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Chart from "react-google-charts"; 3 | 4 | import { Card, CardHeader, Row, CardBody } from "reactstrap"; 5 | 6 | function TicketsPieChart({ focus, userTickets }) { 7 | const [pieChartData, setPieChartData] = useState([]); 8 | 9 | //capitalization function 10 | const capitalize = (s) => { 11 | if (typeof s !== "string") return ""; 12 | return s.charAt(0).toUpperCase() + s.slice(1); 13 | }; 14 | 15 | useEffect(() => { 16 | let isRendered = true; 17 | let map = {}; //{ticketType: quantity} 18 | 19 | userTickets.forEach((ticket) => { 20 | if (map[capitalize(ticket[focus])]) { 21 | map[capitalize(ticket[focus])]++; 22 | } else { 23 | map[capitalize(ticket[focus])] = 1; 24 | } 25 | }); 26 | 27 | if (isRendered) { 28 | setPieChartData(Object.entries(map)); 29 | } 30 | 31 | return (isRendered = false); 32 | }, [userTickets, focus]); 33 | 34 | return ( 35 |
36 | 37 | 38 | 39 |
40 | {/*
41 | Performance 42 |
*/} 43 |

Tickets by {capitalize(focus)}

44 |
45 |
46 |
47 | 48 | Loading Chart
} 53 | data={ 54 | pieChartData.length > 0 55 | ? [ 56 | [`Ticket ${capitalize(focus)}`, "Number of Tickets"], 57 | ...pieChartData, 58 | ] 59 | : [[`Ticket ${capitalize(focus)}`, "Number of Tickets"]] 60 | } 61 | // options={{ 62 | // title: "Tickets by Type", 63 | // }} 64 | rootProps={{ "data-testid": "1" }} 65 | /> 66 | 67 | 68 | 69 | ); 70 | } 71 | 72 | export default TicketsPieChart; 73 | -------------------------------------------------------------------------------- /client/src/components/Footers/AdminFooter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // reactstrap components 4 | import { Row, Col } from "reactstrap"; 5 | 6 | const Footer = () => { 7 | return ( 8 | 25 | ); 26 | }; 27 | 28 | export default Footer; 29 | -------------------------------------------------------------------------------- /client/src/components/Footers/AuthFooter.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Argon Dashboard React - v1.2.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/argon-dashboard-react 8 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 9 | * Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md) 10 | 11 | * Coded by Creative Tim 12 | 13 | ========================================================= 14 | 15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | */ 18 | /*eslint-disable*/ 19 | import React from "react"; 20 | 21 | // reactstrap components 22 | import { NavItem, NavLink, Nav, Container, Row, Col } from "reactstrap"; 23 | 24 | const Login = () => { 25 | return ( 26 | <> 27 | 46 | 47 | ); 48 | }; 49 | 50 | export default Login; 51 | -------------------------------------------------------------------------------- /client/src/components/Forms/AddTeamMember.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import API from "../../utils/API"; 3 | import { Container, Form, FormGroup, Label, Input, Button } from "reactstrap"; 4 | 5 | const AddTeamMember = (props) => { 6 | const { projectId } = props; 7 | const [dbUsers, setDbUsers] = useState([]); 8 | const [selectedUsers, setSelectedUsers] = useState([]); 9 | 10 | useEffect(() => { 11 | const abortController = new AbortController(); 12 | 13 | async function fetchData() { 14 | const users = await API.getAvailableUsers(projectId, abortController); 15 | 16 | setDbUsers(users); 17 | } 18 | 19 | fetchData(); 20 | 21 | return () => { 22 | abortController.abort(); 23 | }; 24 | }, [setSelectedUsers, projectId]); 25 | 26 | const handleChange = (event) => { 27 | let values = Array.from( 28 | event.target.selectedOptions, 29 | (option) => option.value 30 | ); 31 | 32 | setSelectedUsers(values); 33 | }; 34 | 35 | const submit = async (e) => { 36 | e.preventDefault(); 37 | 38 | selectedUsers.forEach(async (userId) => { 39 | await API.addTeamMember(projectId, { userId }); 40 | }); 41 | 42 | const projectTeamRes = await API.getProjectUsers(projectId); 43 | 44 | props.setProjectTeam(projectTeamRes); 45 | 46 | props.toggle(); 47 | }; 48 | 49 | return ( 50 | 51 |
52 | 53 | 54 | 61 | {dbUsers.map((user, key) => { 62 | return ( 63 | 66 | ); 67 | })} 68 | 69 | 72 | 73 |
74 |
75 | ); 76 | }; 77 | 78 | export default AddTeamMember; 79 | -------------------------------------------------------------------------------- /client/src/components/Forms/CreateProject.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | import { 4 | Container, 5 | Row, 6 | Col, 7 | Form, 8 | FormGroup, 9 | Label, 10 | Input, 11 | Button, 12 | } from "reactstrap"; 13 | import API from "../../utils/API"; 14 | 15 | const CreateProject = (props) => { 16 | const [values, setValues] = useState({ name: "", description: "", team: [] }); 17 | const [availableTeamMembers, setAvailableTeamMembers] = useState([]); 18 | 19 | const handleChange = (event) => { 20 | let value; 21 | 22 | if ( 23 | event.target.type === "select" || 24 | event.target.type === "select-multiple" 25 | ) { 26 | value = Array.from( 27 | event.target.selectedOptions, 28 | (option) => option.value 29 | ); 30 | } else { 31 | value = event.target.value; 32 | } 33 | const name = event.target.name; 34 | 35 | setValues({ 36 | ...values, 37 | [name]: value, 38 | }); 39 | }; 40 | 41 | useEffect(() => { 42 | let isRendered = true; 43 | 44 | async function fetchUsers() { 45 | const users = await API.getUsers(); 46 | if (isRendered === true) setAvailableTeamMembers(users); 47 | } 48 | 49 | fetchUsers(); 50 | 51 | return () => { 52 | isRendered = false; 53 | }; 54 | }, []); 55 | 56 | async function submit(event) { 57 | event.preventDefault(); 58 | 59 | try { 60 | let projectId = await API.createProject(values); 61 | 62 | values.team.forEach(async (userId) => { 63 | await API.addTeamMember(projectId.id, { userId }); 64 | }); 65 | } catch (err) { 66 | console.log(err); 67 | } 68 | 69 | setValues({ name: "", description: "", team: [] }); 70 | 71 | props.toggle(); 72 | } 73 | 74 | return ( 75 | 76 |
77 | 78 | 79 | 80 | 86 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 126 | {availableTeamMembers.map((user, key) => { 127 | return ( 128 | 131 | ); 132 | })} 133 | 134 | 135 | 136 | 137 | 138 | 141 |
142 |
143 | ); 144 | }; 145 | 146 | export default CreateProject; 147 | -------------------------------------------------------------------------------- /client/src/components/Forms/UpdateProject.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import { 4 | Container, 5 | Row, 6 | Form, 7 | FormGroup, 8 | Label, 9 | Input, 10 | Button, 11 | } from "reactstrap"; 12 | import API from "../../utils/API"; 13 | 14 | const UpdateProject = (props) => { 15 | const [values, setValues] = useState({ 16 | name: props.projectData.name, 17 | description: props.projectData.description, 18 | team: props.projectTeam, 19 | }); 20 | 21 | const handleChange = (event) => { 22 | let value; 23 | 24 | if ( 25 | event.target.type === "select" || 26 | event.target.type === "select-multiple" 27 | ) { 28 | value = Array.from( 29 | event.target.selectedOptions, 30 | (option) => option.value 31 | ); 32 | } else { 33 | value = event.target.value; 34 | } 35 | const name = event.target.name; 36 | 37 | setValues({ 38 | ...values, 39 | [name]: value, 40 | }); 41 | }; 42 | 43 | async function submit(event) { 44 | event.preventDefault(); 45 | 46 | try { 47 | await API.updateProject(props.projectData.id, values); 48 | await API.removeAllTeamMembers(props.projectData.id); 49 | 50 | values.team.forEach(async (teammateId) => { 51 | await API.addTeamMember(props.projectData.id, { userId: teammateId }); 52 | }); 53 | } catch (err) { 54 | console.log(err); 55 | } 56 | 57 | setValues({ name: "", description: "", team: [] }); 58 | 59 | props.resetProjectId(); 60 | props.toggle(); 61 | } 62 | 63 | if (values.team) { 64 | return ( 65 | 66 |
67 | 68 | 69 | 75 | 84 | 85 | 86 | 87 | 88 | 89 | 98 | 99 | 100 | 101 | 102 | 103 | 111 | {props.allUsers.map((user, key) => { 112 | return ( 113 | 116 | ); 117 | })} 118 | 119 | 120 | 121 | 122 | 125 |
126 |
127 | ); 128 | } else { 129 | return Loading...; 130 | } 131 | }; 132 | 133 | export default UpdateProject; 134 | -------------------------------------------------------------------------------- /client/src/components/Forms/useForm.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { AsYouType } from "libphonenumber-js"; 3 | 4 | const useForm = (callback, initialValues, validate) => { 5 | const [values, setValues] = useState(initialValues); 6 | 7 | //new state for errors 8 | const [errors, setErrors] = useState({}); 9 | 10 | const [isSubmitting, setIsSubmitting] = useState(false); //isolates the submit callback function to only run if there is an error change. 11 | 12 | let asYouType = new AsYouType("US"); 13 | 14 | const handleChange = (event) => { 15 | if (event.target.getAttribute("type") === "phone") { 16 | let phone = asYouType.input(event.target.value); 17 | 18 | setValues({ ...values, phone }); 19 | } else { 20 | let value; 21 | 22 | if (event.target.type === "checkbox") { 23 | value = event.target.checked; 24 | } else if ( 25 | event.target.type === "select" || 26 | event.target.type === "select-multiple" 27 | ) { 28 | value = Array.from( 29 | event.target.selectedOptions, 30 | (option) => option.value 31 | ); 32 | } else { 33 | value = event.target.value; 34 | } 35 | 36 | const name = event.target.name; 37 | 38 | setValues({ 39 | ...values, 40 | [name]: value, 41 | }); 42 | } 43 | }; 44 | 45 | const handleSubmit = (event) => { 46 | event.preventDefault(); 47 | 48 | validate(values).then((res) => { 49 | setIsSubmitting(true); 50 | setErrors(res); //handle validation 51 | }); 52 | }; 53 | 54 | useEffect(() => { 55 | if (Object.keys(errors).length === 0 && isSubmitting) { 56 | callback(); 57 | } 58 | }, [errors]); 59 | 60 | return { 61 | handleChange, 62 | handleSubmit, 63 | values, 64 | errors, 65 | }; 66 | }; 67 | 68 | export default useForm; 69 | -------------------------------------------------------------------------------- /client/src/components/Headers/UserHeader.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Argon Dashboard React - v1.2.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/argon-dashboard-react 8 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 9 | * Licensed under MIT (https://github.com/creativetimofficial/argon-dashboard-react/blob/master/LICENSE.md) 10 | 11 | * Coded by Creative Tim 12 | 13 | ========================================================= 14 | 15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | */ 18 | import React from "react"; 19 | 20 | // reactstrap components 21 | import { Button, Container, Row, Col } from "reactstrap"; 22 | 23 | const UserHeader = () => { 24 | return ( 25 | <> 26 |
38 | {/* Mask */} 39 | 40 | {/* Header container */} 41 | 42 | 43 | 44 |

Hello Jesse

45 |

46 | This is your profile page. You can see the progress you've made 47 | with your work and manage your projects or assigned tasks 48 |

49 | 56 | 57 |
58 |
59 |
60 | 61 | ); 62 | }; 63 | 64 | export default UserHeader; 65 | -------------------------------------------------------------------------------- /client/src/components/Modal/Modal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Button } from "reactstrap"; 3 | import ReactDom from "react-dom"; 4 | 5 | const MODAL_STYLES = { 6 | position: "fixed", 7 | top: "50%", 8 | left: "50%", 9 | transform: "translate(-50%, -50%)", 10 | backgroundColor: "#FFF", 11 | padding: "50px", 12 | width: "800px", 13 | zIndex: 1000, 14 | }; 15 | 16 | const OVERLAY_STYLES = { 17 | position: "fixed", 18 | top: 0, 19 | left: 0, 20 | right: 0, 21 | bottom: 0, 22 | backgroundColor: "rgba(0, 0, 0, 0.7)", 23 | zIndex: 1000, 24 | }; 25 | 26 | export default function Modal({ open, onClose, children }) { 27 | if (!open) return null; 28 | 29 | return ReactDom.createPortal( 30 | <> 31 |
32 |
33 | 36 | {children} 37 |
38 | , 39 | document.getElementById("portal") 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /client/src/components/Navbars/AdminNavbar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | // reactstrap components 3 | 4 | import { Navbar, Container } from "reactstrap"; 5 | 6 | const AdminNavbar = (props) => { 7 | return ( 8 | <> 9 | 10 | 11 |
12 | {props.brandText} 13 |
14 |
15 |
16 | 17 | ); 18 | }; 19 | 20 | export default AdminNavbar; 21 | -------------------------------------------------------------------------------- /client/src/components/Navbars/AuthNavbar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | // reactstrap components 4 | import { 5 | UncontrolledCollapse, 6 | NavbarBrand, 7 | Navbar, 8 | NavItem, 9 | NavLink, 10 | Nav, 11 | Container, 12 | Row, 13 | Col, 14 | } from "reactstrap"; 15 | 16 | const AdminNavbar = () => { 17 | return ( 18 | <> 19 | 20 | 21 | 22 | Bug Tracker 23 | 24 | 27 | 28 |
29 | 30 | 31 | 32 | ... 39 | 40 | 41 | 42 | 46 | 47 | 48 |
49 | 83 |
84 |
85 |
86 | 87 | ); 88 | }; 89 | 90 | export default AdminNavbar; 91 | -------------------------------------------------------------------------------- /client/src/components/Tables/DataTable.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Table } from "reactstrap"; 3 | 4 | //TODO: implement this component with all datatables 5 | 6 | function DataTable({ data }) { 7 | const columns = data[0] && Object.keys(data[0]); 8 | 9 | return ( 10 | 11 | 12 | {data[0] && columns.map((heading) => )} 13 | 14 | 15 | {data.map((row) => ( 16 | 17 | {columns.map((column) => ( 18 | 19 | ))} 20 | 21 | ))} 22 | 23 |
{heading}
{row[column]}
24 | ); 25 | } 26 | 27 | export default DataTable; 28 | -------------------------------------------------------------------------------- /client/src/components/Tables/PaginationComponent.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useMemo } from "react"; 2 | import { Pagination, PaginationItem, PaginationLink } from "reactstrap"; 3 | 4 | export default function PaginationComponent({ 5 | total = 0, 6 | itemsPerPage = 10, 7 | currentPage = 1, 8 | onPageChange, 9 | }) { 10 | const [totalPages, setTotalPages] = useState(0); 11 | 12 | useEffect(() => { 13 | if (total > 0 && itemsPerPage > 0) { 14 | setTotalPages(Math.ceil(total / itemsPerPage)); 15 | } 16 | }, [total, itemsPerPage]); 17 | 18 | const paginationItems = useMemo(() => { 19 | const pages = []; 20 | for (let i = 1; i <= totalPages; i++) { 21 | pages.push( 22 | onPageChange(i)} 26 | > 27 | onPageChange(i)}>{i} 28 | 29 | ); 30 | } 31 | 32 | return pages; 33 | }, [totalPages, currentPage, onPageChange]); 34 | 35 | if (totalPages === 0) return null; 36 | 37 | return ( 38 | 39 | 40 | onPageChange(currentPage - 1)} 43 | disabled={currentPage === 1} 44 | /> 45 | 46 | {paginationItems} 47 | 48 | onPageChange(currentPage + 1)} 51 | disabled={currentPage === totalPages} 52 | /> 53 | 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /client/src/components/Tables/UsersCell.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import API from "../../utils/API"; 3 | import { Row } from "reactstrap"; 4 | 5 | const UsersCell = (props) => { 6 | let [projectUsers, setProjectUsers] = useState([]); 7 | 8 | useEffect(() => { 9 | let isRendered = true; 10 | 11 | API.getProjectUsers(props.projectId).then((json) => { 12 | if (isRendered === true) setProjectUsers(json); 13 | }); 14 | 15 | return () => { 16 | isRendered = false; 17 | }; 18 | }, [props.projectId, props.selectedProjectId]); 19 | 20 | if (projectUsers && projectUsers.length) { 21 | return ( 22 | <> 23 | {projectUsers.map((user) => { 24 | return ( 25 | 26 | {/* e.preventDefault()}> */} 27 | 28 | {user.first_name} {user.last_name} 29 | 30 | {/* */} 31 | 32 | ); 33 | })} 34 | 35 | ); 36 | } 37 | 38 | return ( 39 | <> 40 | 41 | No Users Assigned 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default UsersCell; 48 | -------------------------------------------------------------------------------- /client/src/contexts/AuthContext.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is not currently in use, but might be implemented for a 3 | more global access to auth states 4 | */ 5 | 6 | import React, { useContext, useState, useEffect } from "react"; 7 | 8 | const AuthContext = React.createContext(); 9 | 10 | //call this to access any auth context values 11 | export function useAuth() { 12 | return useContext(AuthContext); 13 | } 14 | 15 | // This is the wrapper for the app that houses the context 16 | export function AuthProvider({ children }) { 17 | const [userAuth, setUserAuth] = useState(""); 18 | 19 | useEffect(() => { 20 | const auth = localStorage.getItem("auth"); 21 | 22 | console.log(auth); 23 | setUserAuth(auth); 24 | }, []); 25 | 26 | const value = { 27 | userAuth, 28 | }; 29 | 30 | return {children}; 31 | } 32 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import "assets/plugins/nucleo/css/nucleo.css"; 5 | import "@fortawesome/fontawesome-free/css/all.min.css"; 6 | import "assets/scss/argon-dashboard-react.scss"; 7 | 8 | import App from "./App"; 9 | 10 | ReactDOM.render(, document.getElementById("root")); 11 | -------------------------------------------------------------------------------- /client/src/layouts/Auth.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useLocation, Route, Switch, Redirect } from "react-router-dom"; 3 | // reactstrap components 4 | import { Container, Row, Col } from "reactstrap"; 5 | 6 | // core components 7 | import AuthFooter from "components/Footers/AuthFooter.js"; 8 | 9 | import routes from "routes.js"; 10 | 11 | const Auth = (props) => { 12 | const mainContent = React.useRef(null); 13 | const location = useLocation(); 14 | 15 | React.useEffect(() => { 16 | document.body.classList.add("bg-default"); 17 | return () => { 18 | document.body.classList.remove("bg-default"); 19 | }; 20 | }, []); 21 | React.useEffect(() => { 22 | document.documentElement.scrollTop = 0; 23 | document.scrollingElement.scrollTop = 0; 24 | mainContent.current.scrollTop = 0; 25 | }, [location]); 26 | 27 | const getRoutes = (routes) => { 28 | return routes.map((prop, key) => { 29 | if (prop.layout === "auth") { 30 | return ( 31 | } 34 | key={key} 35 | /> 36 | ); 37 | } else { 38 | return null; 39 | } 40 | }); 41 | }; 42 | 43 | return ( 44 | <> 45 |
46 |
47 | 48 |
49 | 50 | 51 |

Bug Tracker

52 |

Login or register

53 | 54 |
55 |
56 |
57 |
58 | 66 | 70 | 71 |
72 |
73 | {/* Page content */} 74 | 75 | 76 | 77 | {getRoutes(routes)} 78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | ); 86 | }; 87 | 88 | export default Auth; 89 | -------------------------------------------------------------------------------- /client/src/layouts/General.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useLocation, Route, Switch, Redirect } from "react-router-dom"; 3 | // reactstrap components 4 | import { Container } from "reactstrap"; 5 | // core components 6 | import AdminNavbar from "components/Navbars/AdminNavbar.js"; 7 | import AdminFooter from "components/Footers/AdminFooter.js"; 8 | import GeneralSidebar from "components/Sidebar/GeneralSidebar.js"; 9 | 10 | import routes from "routes.js"; 11 | 12 | const General = (props) => { 13 | const mainContent = React.useRef(null); 14 | const location = useLocation(); 15 | 16 | React.useEffect(() => { 17 | document.documentElement.scrollTop = 0; 18 | document.scrollingElement.scrollTop = 0; 19 | mainContent.current.scrollTop = 0; 20 | }, [location]); 21 | 22 | const getRoutes = (routes) => { 23 | return routes.map((prop, key) => { 24 | if (prop.layout === "general") { 25 | return ( 26 | } 29 | key={key} 30 | /> 31 | ); 32 | } else { 33 | return null; 34 | } 35 | }); 36 | }; 37 | 38 | const getBrandText = (path) => { 39 | for (let i = 0; i < routes.length; i++) { 40 | if (routes[i].path.indexOf(":") !== -1) { 41 | if ( 42 | props.location.pathname.indexOf( 43 | routes[i].layout + 44 | routes[i].path.slice(0, routes[i].path.indexOf(":")) 45 | ) !== -1 46 | ) { 47 | return routes[i].name; 48 | } 49 | } else if ( 50 | props.location.pathname.indexOf(routes[i].layout + routes[i].path) !== 51 | -1 52 | ) { 53 | return routes[i].name; 54 | } 55 | } 56 | return "Brand"; 57 | }; 58 | 59 | return ( 60 | <> 61 |

NON ADMIN

62 | 71 |
72 | 76 | 77 | {getRoutes(routes)} 78 | 79 | 80 | 81 | 82 | 83 |
84 | 85 | ); 86 | }; 87 | 88 | export default General; 89 | -------------------------------------------------------------------------------- /client/src/layouts/Main.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useLocation, Route, Switch, Redirect } from "react-router-dom"; 3 | // reactstrap components 4 | import { Container } from "reactstrap"; 5 | // core components 6 | import AdminNavbar from "components/Navbars/AdminNavbar.js"; 7 | import AdminFooter from "components/Footers/AdminFooter.js"; 8 | import UniversalSidebar from "components/Sidebar/UniversalSidebar.js"; 9 | 10 | // import { useAuth } from "../contexts/AuthContext"; 11 | import routes from "routes.js"; 12 | 13 | const Admin = (props) => { 14 | const mainContent = React.useRef(null); 15 | const location = useLocation(); 16 | 17 | // let auth = useAuth(); 18 | 19 | useEffect(() => { 20 | document.documentElement.scrollTop = 0; 21 | document.scrollingElement.scrollTop = 0; 22 | mainContent.current.scrollTop = 0; 23 | }, [location]); 24 | 25 | const getRoutes = (routes) => { 26 | return routes.map((prop, key) => { 27 | if (prop.layout === "admin") { 28 | return ( 29 | } 32 | key={key} 33 | /> 34 | ); 35 | } else if (prop.layout === "general") { 36 | return ( 37 | } 40 | key={key} 41 | /> 42 | ); 43 | } else { 44 | return null; 45 | } 46 | }); 47 | }; 48 | 49 | const getBrandText = (path) => { 50 | for (let i = 0; i < routes.length; i++) { 51 | if (routes[i].path.indexOf(":") !== -1) { 52 | if ( 53 | props.location.pathname.indexOf( 54 | routes[i].layout + 55 | routes[i].path.slice(0, routes[i].path.indexOf(":")) 56 | ) !== -1 57 | ) { 58 | return routes[i].name; 59 | } 60 | } else if ( 61 | props.location.pathname.indexOf(routes[i].layout + routes[i].path) !== 62 | -1 63 | ) { 64 | return routes[i].name; 65 | } 66 | } 67 | return "Brand"; 68 | }; 69 | 70 | return ( 71 | <> 72 | 81 |
82 | 86 | 87 | {getRoutes(routes)} 88 | 89 | 90 | 91 | 92 | 93 |
94 | 95 | ); 96 | }; 97 | 98 | export default Admin; 99 | -------------------------------------------------------------------------------- /client/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /client/src/routes.js: -------------------------------------------------------------------------------- 1 | import Index from "views/Index.js"; 2 | import Register from "views/Register.js"; 3 | import Login from "views/Login.js"; 4 | import Administration from "views/Administration"; 5 | import Tickets from "views/Tickets"; 6 | import Project from "views/Project"; 7 | 8 | var routes = [ 9 | { 10 | path: "/index", 11 | name: "Dashboard", 12 | icon: "ni ni-tv-2 text-primary", 13 | component: Index, 14 | layout: "general", 15 | root: "/general", 16 | display: true, 17 | }, 18 | { 19 | path: "/tickets", 20 | name: "Tickets", 21 | icon: "ni ni-single-copy-04 text-teal", 22 | component: Tickets, 23 | layout: "general", 24 | root: "/general", 25 | display: true, 26 | }, 27 | { 28 | path: "/index", 29 | name: "Dashboard", 30 | icon: "ni ni-tv-2 text-primary", 31 | component: Index, 32 | layout: "admin", 33 | root: "/admin", 34 | display: true, 35 | }, 36 | { 37 | path: "/tickets", 38 | name: "Tickets", 39 | icon: "ni ni-single-copy-04 text-teal", 40 | component: Tickets, 41 | layout: "admin", 42 | root: "/admin", 43 | display: true, 44 | }, 45 | { 46 | path: "/administration", 47 | name: "Administration", 48 | icon: "ni ni-collection text-red", 49 | component: Administration, 50 | layout: "admin", 51 | root: "/admin", 52 | display: true, 53 | }, 54 | { 55 | path: "/login", 56 | name: "Login", 57 | icon: "ni ni-key-25 text-info", 58 | component: Login, 59 | layout: "auth", 60 | root: "/auth", 61 | }, 62 | { 63 | path: "/register", 64 | name: "Register", 65 | icon: "ni ni-circle-08 text-pink", 66 | component: Register, 67 | layout: "auth", 68 | root: "/auth", 69 | }, 70 | { 71 | path: "/project/:id", 72 | name: "Project", 73 | component: Project, 74 | layout: "general", 75 | root: "/general", 76 | display: false, 77 | }, 78 | { 79 | path: "/project/:id", 80 | name: "Project", 81 | component: Project, 82 | layout: "admin", 83 | root: "/admin", 84 | display: false, 85 | }, 86 | ]; 87 | export default routes; 88 | -------------------------------------------------------------------------------- /client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /client/src/utils/formValidation/loginValidation.js: -------------------------------------------------------------------------------- 1 | export default async function validate(values) { 2 | let errors = {}; 3 | 4 | //Email validation 5 | if (!values.email) { 6 | errors.email = "Email is required"; 7 | } 8 | 9 | //password validation 10 | return errors; 11 | } 12 | -------------------------------------------------------------------------------- /client/src/utils/formValidation/registerValidation.js: -------------------------------------------------------------------------------- 1 | import API from "../API"; 2 | import { isValidPhoneNumber } from "libphonenumber-js"; 3 | // import parsePhoneNumber from "libphonenumber-js"; 4 | 5 | export default async function registerValidation(values) { 6 | let errors = {}; 7 | 8 | //Name validation 9 | if (values.hasOwnProperty("firstName") && !values.firstName) { 10 | errors.firstName = "First name is required"; 11 | } 12 | 13 | if (values.hasOwnProperty("lastName") && !values.lastName) { 14 | errors.lastName = "Last name is required"; 15 | } 16 | 17 | //phone validation 18 | if (values.hasOwnProperty("phone")) { 19 | if (!values.phone) { 20 | errors.phone = "Phone number is required"; 21 | } else if (!isValidPhoneNumber(values.phone, "US")) { 22 | errors.phone = "Please enter valid phone number"; 23 | } 24 | } 25 | 26 | //Email validation 27 | if (values.hasOwnProperty("email")) { 28 | if (!values.email) { 29 | errors.email = "Email address is required"; 30 | } else if (!/\S+@\S+\.\S+/.test(values.email)) { 31 | errors.email = "Email address is invalid"; 32 | } else { 33 | try { 34 | const res = await API.lookupUserByEmail({ email: values.email }); 35 | 36 | if (res.length > 0) { 37 | errors.email = "Email already exists"; 38 | } 39 | } catch (err) { 40 | console.log(err, "Error looking up email in database"); 41 | } 42 | } 43 | } 44 | 45 | // Password validation 46 | if (values.hasOwnProperty("password")) { 47 | if (!values.password) { 48 | errors.password = "Password is required"; 49 | } else if (values.password.length < 8) { 50 | errors.password = "Password needs to be 8 or more characters"; 51 | } 52 | 53 | if (values.password !== values.confirmPassword) { 54 | errors.confirmPassword = "Passwords entered do not match"; 55 | } 56 | } 57 | 58 | // Lease form validation 59 | if (values.hasOwnProperty("property") && !values.property) { 60 | errors.property = "Select a property"; 61 | } 62 | 63 | if (values.hasOwnProperty("startDate") && !values.startDate) { 64 | errors.startDate = "Enter lease start date"; 65 | } 66 | 67 | if (values.hasOwnProperty("endDate") && !values.endDate) { 68 | errors.endDate = "Enter lease end date"; 69 | } 70 | 71 | return errors; //need to make sure promises resolve before returning some how 72 | } 73 | -------------------------------------------------------------------------------- /client/src/utils/formValidation/ticketValidation.js: -------------------------------------------------------------------------------- 1 | export default async function validate(values) { 2 | let errors = {}; 3 | 4 | //Title validation 5 | if (!values.title) { 6 | errors.title = "Title is required"; 7 | } 8 | 9 | return errors; 10 | } 11 | -------------------------------------------------------------------------------- /client/src/views/Index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | // node.js library that concatenates classes (strings) 3 | // import classnames from "classnames"; 4 | // javascipt plugin for creating charts 5 | import Chart from "chart.js"; 6 | // react plugin used to create charts 7 | // import { Line, Bar } from "react-chartjs-2"; 8 | // reactstrap components 9 | import { 10 | // Button, 11 | // Card, 12 | // CardHeader, 13 | // CardBody, 14 | // NavItem, 15 | // NavLink, 16 | // Nav, 17 | // Progress, 18 | // Table, 19 | Container, 20 | Row, 21 | Col, 22 | } from "reactstrap"; 23 | 24 | // core components 25 | import { 26 | chartOptions, 27 | parseOptions, 28 | // chartExample1, 29 | // chartExample2, 30 | } from "variables/charts.js"; 31 | 32 | import Header from "components/Headers/Header.js"; 33 | import ProjectsTable from "../components/Tables/ProjectsTable"; 34 | import TicketsPieChart from "components/Charts/TicketsPieChart"; 35 | import API from "../utils/API"; 36 | 37 | const Index = (props) => { 38 | const [userTickets, setUserTickets] = useState([]); 39 | 40 | if (window.Chart) { 41 | parseOptions(Chart, chartOptions()); 42 | } 43 | 44 | useEffect(() => { 45 | //flag for async useEffect cleanup 46 | const abortController = new AbortController(); 47 | 48 | //TODO: fetch user tickets. create backend and front end route 49 | async function fetchUserTickets() { 50 | try { 51 | const userTicketsRes = await ( 52 | await API.getUserTickets(abortController) 53 | ).json(); 54 | 55 | setUserTickets(userTicketsRes); 56 | } catch (err) { 57 | if (!abortController.signal.aborted) { 58 | console.log("Error fetching user tickets", err); 59 | } 60 | } 61 | } 62 | 63 | fetchUserTickets(); 64 | return () => { 65 | abortController.abort(); 66 | }; 67 | }, []); 68 | 69 | return ( 70 | <> 71 |
72 | {/* Page content */} 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ); 94 | }; 95 | 96 | export default Index; 97 | -------------------------------------------------------------------------------- /client/src/views/Tables.css: -------------------------------------------------------------------------------- 1 | .ticketRow:hover { 2 | background-color: #1592ee; 3 | color: #f8fbfd; 4 | cursor: pointer; 5 | } 6 | 7 | .listItem:hover { 8 | background-color: #f4f5f7; 9 | color: black; 10 | cursor: pointer; 11 | } -------------------------------------------------------------------------------- /controllers/availableUsersController.js: -------------------------------------------------------------------------------- 1 | const pool = require("../db"); 2 | 3 | module.exports = { 4 | getAvailableUsers: async function (req, res) { 5 | const client = await pool.connect(); 6 | 7 | const { projectId } = req.params; 8 | 9 | try { 10 | const { 11 | rows, 12 | } = await client.query( 13 | "SELECT id, email, first_name, last_name FROM users as U WHERE NOT EXISTS (SELECT user_id FROM user_projects as UP WHERE UP.user_id = U.id AND UP.project_id = $1)", 14 | [projectId] 15 | ); 16 | 17 | res.status(201).json(rows); 18 | } catch (err) { 19 | console.log(err); 20 | res.send(500).json({ msg: "Failed to fetch available users" }); 21 | } 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /controllers/commentController.js: -------------------------------------------------------------------------------- 1 | const pool = require("../db"); 2 | 3 | module.exports = { 4 | createComment: async (req, res) => { 5 | const { ticketId } = req.params; 6 | const { comment } = req.body; 7 | const authorId = req.user; 8 | const client = await pool.connect(); 9 | 10 | try { 11 | await client.query( 12 | "INSERT INTO comments (author_id, ticket_id, comment) VALUES ($1, $2, $3)", 13 | [authorId, ticketId, comment] 14 | ); 15 | 16 | res.status(201).json({ msg: `Comment on ticket${ticketId} created` }); 17 | } catch (err) { 18 | console.log(`Failed to create message for ${ticketId}: `, "\n", err); 19 | res.status(500).json({ msg: `Please review query` }); 20 | } finally { 21 | await client.release(); 22 | } 23 | }, 24 | getTicketComments: async (req, res) => { 25 | const { ticketId } = req.params; 26 | const client = await pool.connect(); 27 | 28 | try { 29 | const { rows } = await client.query( 30 | "SELECT comments.id, comments.ticket_id, comments.comment, comments.created_at, users.id AS author_id, users.first_name, users.last_name FROM comments JOIN users ON comments.author_id = users.id WHERE ticket_id = $1", 31 | [ticketId] 32 | ); 33 | 34 | res.json(rows); 35 | } catch (err) { 36 | console.log(`Failed to get ticket for`, "\n", err); 37 | res.status(500).json({ msg: `Please review query` }); 38 | } finally { 39 | client.release(); 40 | } 41 | }, 42 | updateComment: async (req, res) => { 43 | const { ticketId, commentId } = req.params; 44 | const { authorId, comment } = req.body; 45 | const client = await pool.connect(); 46 | 47 | try { 48 | await client.query( 49 | "UPDATE comments SET (author_id, comment) = ($1, $2) WHERE id = $3", 50 | [authorId, comment, commentId] 51 | ); 52 | 53 | res 54 | .status(201) 55 | .json({ msg: `Comment ${commentId} updated successfully` }); 56 | } catch (err) { 57 | console.log(`Failed to update comment: `, "\n", err); 58 | res.status(500).json({ msg: `Please review query` }); 59 | } finally { 60 | await client.release(); 61 | } 62 | }, 63 | deleteComment: async (req, res) => { 64 | const { commentId } = req.params; 65 | const client = await pool.connect(); 66 | 67 | try { 68 | await client.query("DELETE FROM comments WHERE id = $1", [commentId]); 69 | 70 | res.status(200).json({ msg: `Comment ${commentId} deleted` }); 71 | } catch (err) { 72 | console.log("Failed to delete comment: ", "\n", err); 73 | res.status(500).json({ msg: "Review deletion query" }); 74 | } finally { 75 | client.release(); 76 | } 77 | }, 78 | }; 79 | -------------------------------------------------------------------------------- /controllers/devAssignmentsController.js: -------------------------------------------------------------------------------- 1 | const pool = require("../db"); 2 | 3 | module.exports = { 4 | assignDev: async (req, res) => { 5 | const { ticketId } = req.params; 6 | const { devId } = req.body; // If there are multiple users to assign, this will be handled on front end 7 | const client = await pool.connect(); 8 | 9 | try { 10 | await client.query( 11 | "INSERT INTO dev_assignments (ticket_id, user_id) VALUES ($1, $2)", 12 | [ticketId, devId] 13 | ); 14 | 15 | res 16 | .status(201) 17 | .json({ msg: `User ${devId} assigned to ${ticketId} succesfully` }); 18 | } catch (err) { 19 | console.log("assignUsers query error: ", err); 20 | res 21 | .status(400) 22 | .json({ msg: "Please review user project assign creation query" }); 23 | } finally { 24 | await client.release(); 25 | } 26 | }, 27 | 28 | removeDev: async (req, res) => { 29 | const { devId } = req.body; 30 | const client = await pool.connect(); 31 | 32 | try { 33 | await client.query("DELETE FROM dev_assignments WHERE id = $1", [devId]); 34 | 35 | res.json(`User removed from ticket`); 36 | } catch (err) { 37 | console.log("getProject query error: ", err); 38 | res 39 | .status(500) 40 | .json({ msg: "Unable to remove dev assignment from database" }); 41 | } finally { 42 | await client.release(); 43 | } 44 | }, 45 | getAssignedDevs: async (req, res) => { 46 | const { ticketId } = req.params; 47 | const client = await pool.connect(); 48 | 49 | try { 50 | const { 51 | rows, 52 | } = await client.query( 53 | "SELECT user_id, first_name, last_name, phone, email FROM users JOIN dev_assignments ON (dev_assignments.user_id = users.id) WHERE ticket_id = $1", 54 | [ticketId] 55 | ); 56 | 57 | res.json(rows); 58 | } catch (err) { 59 | console.log("getProjectUsers query error: ", err); 60 | res.status(400).json({ msg: "Please review query" }); 61 | } finally { 62 | client.release(); 63 | } 64 | }, 65 | removeAllDevs: async (req, res) => { 66 | const { ticketId } = req.params; 67 | const client = await pool.connect(); 68 | 69 | try { 70 | await client.query("DELETE FROM dev_assignments WHERE ticket_id = $1", [ 71 | ticketId, 72 | ]); 73 | 74 | res.status(204).json({ msg: "All devs removed from ticket" }); 75 | } catch (err) { 76 | console.error(err.message); 77 | res.status(500); 78 | } finally { 79 | client.release(); 80 | } 81 | }, 82 | }; 83 | -------------------------------------------------------------------------------- /controllers/userProjectController.js: -------------------------------------------------------------------------------- 1 | const pool = require("../db"); 2 | 3 | module.exports = { 4 | assignUser: async (req, res) => { 5 | const { projectId } = req.params; 6 | const { userId } = req.body; // If there are multiple users to assign, this will be handled on front end 7 | const client = await pool.connect(); 8 | 9 | try { 10 | await client.query( 11 | "INSERT INTO user_projects (project_id, user_id) VALUES ($1, $2)", 12 | [projectId, userId] 13 | ); 14 | 15 | res 16 | .status(201) 17 | .json({ msg: `User ${userId} assigned to ${projectId} succesfully` }); 18 | } catch (err) { 19 | console.log("assignUsers query error: ", err); 20 | res 21 | .status(400) 22 | .json({ msg: "Please review user project assign creation query" }); 23 | } finally { 24 | await client.release(); 25 | } 26 | }, 27 | removeAllUsers: async (req, res) => { 28 | const { projectId } = req.params; 29 | const client = await pool.connect(); 30 | 31 | try { 32 | await client.query("DELETE FROM user_projects WHERE project_id = $1", [ 33 | projectId, 34 | ]); 35 | 36 | res.status(202).json(`Project users deleted`); 37 | } catch (err) { 38 | console.log("getProject query error: ", err); 39 | res 40 | .status(500) 41 | .json({ msg: "Unable to remove user_project from database" }); 42 | } finally { 43 | await client.release(); 44 | } 45 | }, 46 | removeUser: async (req, res) => { 47 | const { projectId, userId } = req.params; 48 | const client = await pool.connect(); 49 | 50 | try { 51 | await client.query( 52 | "DELETE FROM user_projects WHERE project_id = $1 AND user_id = $2", 53 | [projectId, userId] 54 | ); 55 | 56 | res.status(202).json(`User removed from project`); 57 | } catch (err) { 58 | console.log("getProject query error: ", err); 59 | res 60 | .status(500) 61 | .json({ msg: "Unable to remove user_project from database" }); 62 | } finally { 63 | await client.release(); 64 | } 65 | }, 66 | getProjectUsers: async (req, res) => { 67 | const { projectId } = req.params; 68 | const client = await pool.connect(); 69 | 70 | try { 71 | const { 72 | rows, 73 | } = await client.query( 74 | "SELECT user_id, first_name, last_name, phone, email FROM users JOIN user_projects ON (user_projects.user_id = users.id) WHERE project_id = $1", 75 | [projectId] 76 | ); 77 | 78 | res.json(rows); 79 | } catch (err) { 80 | console.log("getProjectUsers query error: ", err); 81 | res.status(400).json({ msg: "Please review query" }); 82 | } finally { 83 | client.release(); 84 | } 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | const Pool = require("pg").Pool; 2 | require("dotenv").config(); 3 | 4 | const devConfig = { 5 | user: process.env.PGUSER, 6 | host: process.env.PGHOST, 7 | database: process.env.PGDATABASE, 8 | password: process.env.PGPASSWORD, 9 | port: process.env.PGPORT, 10 | max: 20, 11 | connectionTimeoutMillis: 0, 12 | idleTimeoutMillis: 0, 13 | }; 14 | 15 | const proConfig = { 16 | connectionString: process.env.DATABASE_URL, //heroku addons 17 | }; 18 | 19 | const pool = new Pool( 20 | process.env.NODE_ENV === "production" ? proConfig : devConfig 21 | ); 22 | 23 | // the pool will emit an error on behalf of any idle clients 24 | // it contains if a backend error or network partition happens 25 | pool.on("error", (err, client) => { 26 | console.error("Unexpected error on idle client", err); 27 | process.exit(-1); 28 | }); 29 | 30 | module.exports = pool; 31 | -------------------------------------------------------------------------------- /middleware/authorization.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | require("dotenv").config(); 3 | 4 | module.exports = async (req, res, next) => { 5 | try { 6 | const jwtToken = req.header("token"); 7 | 8 | if (!jwtToken) { 9 | return res.status(403).json("Not Authorized"); 10 | } 11 | 12 | const payload = jwt.verify(jwtToken, process.env.JWT_SECRET); 13 | 14 | req.user = payload.user; 15 | 16 | next(); 17 | } catch (err) { 18 | console.log(err); 19 | // console.error(err.message); 20 | return res.status(403).json("Not Authorized"); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bug-tracker-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "engines": { 7 | "node": "12.22.1", 8 | "npm": "6.14.12" 9 | }, 10 | "scripts": { 11 | "test": "test", 12 | "install-client": "npm install", 13 | "devStart": "concurrently \"npm run server\" \"cd client && npm start\"", 14 | "start": "node server.js", 15 | "heroku-postbuild": "cd client && npm install && npm run build", 16 | "server": "nodemon server.js" 17 | }, 18 | "author": "Connor Lee", 19 | "license": "ISC", 20 | "dependencies": { 21 | "bcryptjs": "^2.4.3", 22 | "cors": "^2.8.5", 23 | "dotenv": "^8.2.0", 24 | "express": "^4.17.1", 25 | "jsonwebtoken": "^8.5.1", 26 | "libphonenumber-js": "^1.9.17", 27 | "moment": "^2.27.0", 28 | "pg": "^8.3.0", 29 | "react-google-charts": "^3.0.15", 30 | "react-toastify": "^7.0.4" 31 | }, 32 | "devDependencies": { 33 | "concurrently": "^5.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /routes/api/auth.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const userController = require("../../controllers/userController"); 3 | 4 | // Matches route with "/api/auth/user" 5 | router.route(`/user`).post(userController.lookupUserByEmail); 6 | 7 | module.exports = router; 8 | -------------------------------------------------------------------------------- /routes/api/availableUsers.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const availableUsersController = require("../../controllers/availableUsersController"); 3 | 4 | router 5 | // Matches route with "/api/availableUsers/:projectId" 6 | .route("/:projectId") 7 | .get(availableUsersController.getAvailableUsers); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/api/comment.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const commentController = require("../../controllers/commentController"); 3 | const authorization = require("../../middleware/authorization"); 4 | 5 | // Matches route with "/api/comments/" 6 | router 7 | .route("/:ticketId") 8 | .post(authorization, commentController.createComment) 9 | .get(authorization, commentController.getTicketComments); 10 | 11 | router 12 | .route("/:ticketId/:commentId") 13 | .put(authorization, commentController.updateComment) 14 | .delete(authorization, commentController.deleteComment); 15 | 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /routes/api/devAssignments.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const devAssignmentsController = require("../../controllers/devAssignmentsController"); 3 | 4 | // Matches route with "/api/assigneddev/" 5 | router.route("/").delete(devAssignmentsController.removeDev); 6 | 7 | // Matches route with "/api/assigneddev/:ticketId" 8 | router 9 | .route("/:ticketId") 10 | .post(devAssignmentsController.assignDev) 11 | .get(devAssignmentsController.getAssignedDevs) 12 | .delete(devAssignmentsController.removeAllDevs); 13 | 14 | module.exports = router; 15 | -------------------------------------------------------------------------------- /routes/api/index.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const projectRoutes = require("./project"); 3 | const userRoutes = require("./user"); 4 | const userProjectRoutes = require("./userProjects"); 5 | const ticketRoutes = require("./ticket"); 6 | const commentRoutes = require("./comment"); 7 | const devAssignmentsRoutes = require("./devAssignments"); 8 | const loginRoutes = require("./login"); 9 | const availableUsersRoutes = require("./availableUsers"); 10 | const authRoutes = require("./auth"); 11 | 12 | router.use("/projects", projectRoutes); 13 | router.use("/users", userRoutes); 14 | router.use("/userprojects", userProjectRoutes); 15 | router.use("/tickets", ticketRoutes); 16 | router.use("/comments", commentRoutes); 17 | router.use("/devassignments", devAssignmentsRoutes); 18 | router.use("/login", loginRoutes); 19 | router.use("/availableUsers", availableUsersRoutes); 20 | router.use("/auth", authRoutes); 21 | 22 | module.exports = router; 23 | -------------------------------------------------------------------------------- /routes/api/login.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const pool = require("../../db"); 3 | const bcrypt = require("bcryptjs"); 4 | const jwtGenerator = require("../../utils/jwtGenerator"); 5 | 6 | //Matches route /login 7 | router.post("/", async (req, res) => { 8 | const client = await pool.connect(); 9 | 10 | try { 11 | //1. destructure req.body 12 | const { email, password } = req.body; 13 | 14 | //2. check if user doesn't exist (throw error if not) 15 | const user = await client.query("SELECT * FROM users WHERE email = $1", [ 16 | email, 17 | ]); 18 | if (user.rows.length === 0) { 19 | return res.status(401).send("Email or password is incorrect"); 20 | } 21 | 22 | //3. check if incoming password is correct 23 | const validPassword = await bcrypt.compare( 24 | password, 25 | user.rows[0].password_hash 26 | ); 27 | if (!validPassword) { 28 | return res.status(401).send("Email or password is incorrect"); 29 | } 30 | 31 | //4. give jwt token 32 | const token = jwtGenerator(user.rows[0].id); 33 | 34 | res.json({ 35 | token, 36 | auth: user.rows[0].user_authority, 37 | }); 38 | } catch (err) { 39 | console.error(err.message); 40 | } finally { 41 | client.release(); 42 | } 43 | }); 44 | 45 | module.exports = router; 46 | -------------------------------------------------------------------------------- /routes/api/project.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const projectController = require("../../controllers/projectController"); 3 | const authorization = require("../../middleware/authorization"); 4 | 5 | // Matches route with "/api/projects/" 6 | router 7 | .route("/") 8 | .get( 9 | // authorization, 10 | projectController.getAll 11 | ) 12 | .post(authorization, projectController.createProject); 13 | 14 | router 15 | .route("/:id") 16 | .get(authorization, projectController.getProject) 17 | .put(authorization, projectController.updateProject) 18 | .delete(authorization, projectController.deleteProject); 19 | 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /routes/api/ticket.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const ticketController = require("../../controllers/ticketController"); 3 | const authorization = require("../../middleware/authorization"); 4 | 5 | // Matches route with "/api/tickets/" 6 | router.route("/").get(authorization, ticketController.getUserTickets); 7 | 8 | router 9 | .route("/:projectId") 10 | .post(authorization, ticketController.createTicket) 11 | .get(ticketController.getProjectTickets); 12 | 13 | router 14 | .route("/:projectId/:ticketId") 15 | .get(ticketController.getTicket) 16 | .put(authorization, ticketController.updateTicket) 17 | .delete(authorization, ticketController.deleteTicket); 18 | 19 | module.exports = router; 20 | -------------------------------------------------------------------------------- /routes/api/user.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const userController = require("../../controllers/userController"); 3 | const authorization = require("../../middleware/authorization"); 4 | 5 | // Matches route with "/api/users/" 6 | router.route("/").get(userController.getAll).post(userController.addUser); 7 | 8 | // Matches route with "/api/users/:id" 9 | router 10 | .route("/:id") 11 | .get(userController.getUser) 12 | .put(authorization, userController.updateUser) 13 | .delete(authorization, userController.deleteUser); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /routes/api/userProjects.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const userProjectController = require("../../controllers/userProjectController"); 3 | const authorization = require("../../middleware/authorization"); 4 | 5 | // Matches route with "/api/userProjects/" 6 | router.route("/"); 7 | 8 | // Matches route with "/api/userProjects/:projectId" 9 | router 10 | .route("/:projectId") 11 | .post(authorization, userProjectController.assignUser) 12 | .get(authorization, userProjectController.getProjectUsers) 13 | .delete(authorization, userProjectController.removeAllUsers); 14 | 15 | // Matches route with "/api/userprojects/:projectId/:userId" 16 | router 17 | .route("/:projectId/:userId") 18 | .delete(authorization, userProjectController.removeUser); 19 | 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router(); 2 | const apiRoutes = require("./api"); 3 | const authorization = require("../middleware/authorization"); 4 | 5 | // API Routes 6 | router.use("/api", apiRoutes); 7 | 8 | // Auth Routes 9 | router.get("/auth/verify", authorization, async (req, res) => { 10 | try { 11 | res.json(true); 12 | } catch (err) { 13 | console.error(err.message); 14 | res.status(500).send("Server Error"); 15 | } 16 | }); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const routes = require("./routes"); 4 | const path = require("path"); 5 | 6 | const PORT = process.env.PORT || 3001; 7 | require("dotenv").config(); 8 | const cors = require("cors"); 9 | 10 | app.use(express.json()); // middleware to acces req.body 11 | app.use(express.urlencoded({ extended: false })); 12 | //middleware to handle any CORS issues 13 | app.use(cors()); 14 | app.use((req, res, next) => { 15 | res.header("Access-Control-Allow-Origin", "*"); 16 | res.header( 17 | "Access-Control-Allow-Headers", 18 | "Origin, X-Requested-With, Content-Type, Accept, Authorization, token" 19 | ); 20 | if (req.method === "options") { 21 | res.header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, PATCH"); 22 | return res.status(200).json({}); 23 | } 24 | next(); 25 | }); 26 | 27 | app.use(routes); 28 | 29 | if (process.env.NODE_ENV === "production") { 30 | app.use(express.static(path.join(__dirname, "client/build"))); 31 | } 32 | 33 | app.get("*", (req, res) => { 34 | res.sendFile(path.join(__dirname, "client/build/index.html")); 35 | }); 36 | 37 | app.listen(PORT, () => { 38 | console.log(`API Server now listening on port ${PORT}`); 39 | }); 40 | -------------------------------------------------------------------------------- /utils/jwtGenerator.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | require("dotenv").config(); 3 | 4 | function jwtGenerator(user_id) { 5 | const payload = { 6 | user: user_id, 7 | }; 8 | 9 | return jwt.sign( 10 | payload, 11 | process.env.JWT_SECRET 12 | // { expiresIn: "1hr" } 13 | ); 14 | } 15 | 16 | module.exports = jwtGenerator; 17 | --------------------------------------------------------------------------------