├── .docker
├── .dockerignore
├── Dockerfile
├── bin
│ ├── apachelinker.sh
│ └── entrypoint.sh
└── conf
│ ├── .bashrc
│ └── apache.conf
├── .editorconfig
├── .env.example
├── .env.testing.example
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .rsync-filter
├── .styleci.yml
├── API.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── apache.conf
├── app
├── Console
│ ├── Commands
│ │ ├── CompanyClientCommand.php
│ │ ├── FixNoCompany.php
│ │ └── SetupApplicationCommand.php
│ └── Kernel.php
├── Constants
│ ├── BoardKeys.php
│ ├── BoardListsKeys.php
│ ├── CardTypes.php
│ ├── LabelKeys.php
│ ├── RouteConstants.php
│ ├── TeamKeys.php
│ └── UserStoryStatuses.php
├── Events
│ └── UserRegisteredEvent.php
├── Exceptions
│ └── Handler.php
├── Http
│ ├── Controllers
│ │ ├── Auth
│ │ │ ├── ConfirmPasswordController.php
│ │ │ ├── ForgotPasswordController.php
│ │ │ ├── LoginController.php
│ │ │ ├── RegisterController.php
│ │ │ ├── ResetPasswordController.php
│ │ │ └── VerificationController.php
│ │ ├── BacklogLabelController.php
│ │ ├── BoardController.php
│ │ ├── BoardListController.php
│ │ ├── CardController.php
│ │ ├── Controller.php
│ │ ├── EventController.php
│ │ ├── GoalController.php
│ │ ├── LabelController.php
│ │ ├── MemberController.php
│ │ ├── MilestoneController.php
│ │ ├── ProcessController.php
│ │ ├── SprintReportController.php
│ │ ├── TeamController.php
│ │ ├── UserController.php
│ │ └── WorkspaceController.php
│ ├── Kernel.php
│ ├── Middleware
│ │ ├── Authenticate.php
│ │ ├── CheckForMaintenanceMode.php
│ │ ├── EnableCompanyGlobalScope.php
│ │ ├── EncryptCookies.php
│ │ ├── RedirectIfAuthenticated.php
│ │ ├── ResolveCompanyTenant.php
│ │ ├── TrimStrings.php
│ │ ├── TrustProxies.php
│ │ └── VerifyCsrfToken.php
│ ├── Requests
│ │ └── StoreCardRequest.php
│ └── Resources
│ │ ├── BacklogLabelResource.php
│ │ ├── CardResource.php
│ │ ├── LabelResource.php
│ │ ├── MemberResource.php
│ │ ├── MilestoneResource.php
│ │ ├── ProcessResource.php
│ │ ├── SprintReportResource.php
│ │ ├── TeamResource.php
│ │ └── WorkspaceResource.php
├── Library
│ └── Utils
│ │ └── BagOfConstants.php
├── Listeners
│ └── SendWelcomingEmail.php
├── Mail
│ └── WelcomeMail.php
├── Models
│ ├── BacklogLabel.php
│ ├── Board.php
│ ├── BoardList.php
│ ├── Card.php
│ ├── Company.php
│ ├── CompanyClient.php
│ ├── Event.php
│ ├── Goal.php
│ ├── Label.php
│ ├── Member.php
│ ├── Milestone.php
│ ├── Process.php
│ ├── Scopes
│ │ └── CompanyScope.php
│ ├── SprintReport.php
│ ├── Team.php
│ ├── TeamMember.php
│ └── Workspace.php
├── Observers
│ ├── CardObserver.php
│ └── TeamObserver.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── BroadcastServiceProvider.php
│ ├── EventServiceProvider.php
│ └── RouteServiceProvider.php
├── Services
│ ├── BoardListService.php
│ ├── CardService.php
│ └── WorkspaceService.php
└── User.php
├── artisan
├── bootstrap
├── app.php
└── cache
│ └── .gitignore
├── composer.json
├── composer.lock
├── config
├── app.php
├── auth.php
├── broadcasting.php
├── cache.php
├── database.php
├── filesystems.php
├── hashing.php
├── logging.php
├── mail.php
├── queue.php
├── services.php
├── session.php
└── view.php
├── database
├── .gitignore
├── factories
│ ├── BoardListFactory.php
│ ├── CardFactory.php
│ ├── CompanyFactory.php
│ ├── MemberFactory.php
│ ├── TeamFactory.php
│ ├── UserFactory.php
│ └── WorkspaceFactory.php
├── migrations
│ ├── 2014_10_12_000000_create_users_table.php
│ └── 2014_10_12_100000_create_password_resets_table.php
└── seeds
│ └── DatabaseSeeder.php
├── deploy.bash
├── docker-compose.yml
├── init-infra.sh
├── mongo_indexs.js
├── on-server.sh
├── package-lock.json
├── package.json
├── phpcs.xml
├── phpunit.xml
├── public
├── .htaccess
├── favicon.ico
├── images
│ ├── christmas-divider.png
│ ├── elofy.png
│ ├── empty-list.svg
│ ├── favicon.ico
│ ├── logo-dark.svg
│ ├── logo-natal.png
│ ├── logo.svg
│ └── sysvale-logo.svg
├── index.php
└── robots.txt
├── requirements.txt
├── resources
├── js
│ ├── bootstrap.js
│ ├── core
│ │ ├── constants
│ │ │ ├── milestoneStatuses.js
│ │ │ └── userStoryStatuses.js
│ │ └── utils
│ │ │ ├── convertKeysToCamelCase.js
│ │ │ ├── convertKeysToSnakeCase.js
│ │ │ ├── generateUUID.js
│ │ │ ├── makeBoardStore.js
│ │ │ ├── makeCrudServices.js
│ │ │ ├── makeFormFields.js
│ │ │ ├── makeMutations.js
│ │ │ ├── makeRequestStore.js
│ │ │ ├── requestsStates
│ │ │ ├── failureState.js
│ │ │ ├── index.js
│ │ │ ├── initialState.js
│ │ │ ├── requestState.js
│ │ │ └── successState.js
│ │ │ └── upperCamelCase.js
│ ├── http.js
│ ├── modules
│ │ ├── board
│ │ │ ├── app.js
│ │ │ ├── components
│ │ │ │ ├── AcceptanceCriteriaForm.vue
│ │ │ │ ├── ArtifactItem.vue
│ │ │ │ ├── ArtifactsForm.vue
│ │ │ │ ├── Board.vue
│ │ │ │ ├── BoardContainer.vue
│ │ │ │ ├── BoardContent.vue
│ │ │ │ ├── Card.vue
│ │ │ │ ├── ChecklistForm.vue
│ │ │ │ ├── EventsBoard.vue
│ │ │ │ ├── InlineDeleteConfirmation.vue
│ │ │ │ ├── LabelItem.vue
│ │ │ │ ├── LabelList.vue
│ │ │ │ ├── LabelSelect.vue
│ │ │ │ ├── LinkChip.vue
│ │ │ │ ├── List.vue
│ │ │ │ ├── ListContainer.vue
│ │ │ │ ├── ListSkeletonLoader.vue
│ │ │ │ ├── MemberItem.vue
│ │ │ │ ├── MemberList.vue
│ │ │ │ ├── MemberSelect.vue
│ │ │ │ ├── SprintBacklogOverview.vue
│ │ │ │ ├── SprintTabContent.vue
│ │ │ │ ├── SwitchButton.vue
│ │ │ │ ├── TeamChip.vue
│ │ │ │ ├── TooltipRating.vue
│ │ │ │ ├── UserStoriesBoard.vue
│ │ │ │ ├── UserStoryPipeline.vue
│ │ │ │ └── UserStoryPipelineItem.vue
│ │ │ ├── constants
│ │ │ │ ├── BoardKeys.js
│ │ │ │ ├── BoardListKeys.js
│ │ │ │ ├── CardTypes.js
│ │ │ │ ├── LabelKeys.js
│ │ │ │ └── PlanningGroups.js
│ │ │ ├── routes
│ │ │ │ └── index.js
│ │ │ ├── services
│ │ │ │ ├── boards.js
│ │ │ │ ├── cards.js
│ │ │ │ ├── events.js
│ │ │ │ ├── goals.js
│ │ │ │ ├── planning.js
│ │ │ │ ├── sprint.js
│ │ │ │ ├── sprintReport.js
│ │ │ │ └── userStories.js
│ │ │ ├── store
│ │ │ │ ├── boards.js
│ │ │ │ ├── cards.js
│ │ │ │ ├── events.js
│ │ │ │ ├── goals.js
│ │ │ │ ├── index.js
│ │ │ │ ├── planning.js
│ │ │ │ ├── sprint.js
│ │ │ │ └── sprintReport.js
│ │ │ └── views
│ │ │ │ ├── App.vue
│ │ │ │ ├── CompanyPlanning.vue
│ │ │ │ ├── Home.vue
│ │ │ │ ├── Planning.vue
│ │ │ │ ├── Sprint.vue
│ │ │ │ └── WorkspaceSelection.vue
│ │ ├── milestones
│ │ │ ├── components
│ │ │ │ ├── MilestoneSelect.vue
│ │ │ │ └── TeamsSelect.vue
│ │ │ ├── routes
│ │ │ │ └── index.js
│ │ │ ├── services
│ │ │ │ └── milestones.js
│ │ │ ├── store
│ │ │ │ ├── index.js
│ │ │ │ └── milestones.js
│ │ │ └── views
│ │ │ │ ├── Board.vue
│ │ │ │ └── Home.vue
│ │ ├── processes
│ │ │ ├── components
│ │ │ │ ├── CardFromProcessModal.vue
│ │ │ │ ├── ChecklistFromProcessSelect.vue
│ │ │ │ ├── ProcessForm.vue
│ │ │ │ └── ViewProcessModal.vue
│ │ │ ├── routes
│ │ │ │ └── index.js
│ │ │ ├── services
│ │ │ │ └── processes.js
│ │ │ ├── store
│ │ │ │ ├── index.js
│ │ │ │ └── processes.js
│ │ │ └── views
│ │ │ │ ├── Create.vue
│ │ │ │ ├── Edit.vue
│ │ │ │ └── Home.vue
│ │ ├── reports
│ │ │ ├── components
│ │ │ │ ├── SprintReportDialog.vue
│ │ │ │ └── SprintReportDialogContent.vue
│ │ │ ├── routes
│ │ │ │ └── index.js
│ │ │ ├── services
│ │ │ │ └── sprintReports.js
│ │ │ ├── store
│ │ │ │ ├── index.js
│ │ │ │ └── sprintReports.js
│ │ │ └── views
│ │ │ │ ├── Home.vue
│ │ │ │ └── SprintReport.vue
│ │ └── settings
│ │ │ ├── routes
│ │ │ └── index.js
│ │ │ ├── services
│ │ │ ├── backlogLabels.js
│ │ │ ├── labels.js
│ │ │ ├── members.js
│ │ │ ├── teams.js
│ │ │ └── workspaces.js
│ │ │ ├── store
│ │ │ ├── backlogLabels.js
│ │ │ ├── index.js
│ │ │ ├── labels.js
│ │ │ ├── members.js
│ │ │ ├── teams.js
│ │ │ └── workspaces.js
│ │ │ └── views
│ │ │ ├── BacklogLabels.vue
│ │ │ ├── Home.vue
│ │ │ ├── Labels.vue
│ │ │ ├── Members.vue
│ │ │ ├── Teams.vue
│ │ │ └── Workspaces.vue
│ └── vuetify.js
├── lang
│ └── en
│ │ ├── auth.php
│ │ ├── pagination.php
│ │ ├── passwords.php
│ │ └── validation.php
├── sass
│ ├── _variables.scss
│ ├── app.scss
│ └── home.scss
└── views
│ ├── auth
│ ├── login.blade.php
│ ├── passwords
│ │ ├── confirm.blade.php
│ │ ├── email.blade.php
│ │ └── reset.blade.php
│ ├── register.blade.php
│ └── verify.blade.php
│ ├── index.blade.php
│ ├── layouts
│ └── app.blade.php
│ └── vendor
│ ├── emails
│ └── welcome.blade.php
│ └── mail
│ ├── html
│ ├── button.blade.php
│ ├── footer.blade.php
│ ├── header.blade.php
│ ├── layout.blade.php
│ ├── message.blade.php
│ ├── panel.blade.php
│ ├── promotion.blade.php
│ ├── promotion
│ │ └── button.blade.php
│ ├── subcopy.blade.php
│ ├── table.blade.php
│ └── themes
│ │ └── default.css
│ └── text
│ ├── button.blade.php
│ ├── footer.blade.php
│ ├── header.blade.php
│ ├── layout.blade.php
│ ├── message.blade.php
│ ├── panel.blade.php
│ ├── promotion.blade.php
│ ├── promotion
│ └── button.blade.php
│ ├── subcopy.blade.php
│ └── table.blade.php
├── routes
├── api.php
├── channels.php
├── console.php
└── web.php
├── server.php
├── storage
├── app
│ ├── .gitignore
│ └── public
│ │ └── .gitignore
├── framework
│ ├── .gitignore
│ ├── cache
│ │ ├── .gitignore
│ │ └── data
│ │ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ ├── testing
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
├── tests
├── CreatesApplication.php
├── Feature
│ └── App
│ │ ├── Console
│ │ └── Commands
│ │ │ ├── CompanyClientCommandTest.php
│ │ │ └── FixNoCompanyTest.php
│ │ ├── Http
│ │ └── Controllers
│ │ │ ├── CardControllerTest.php
│ │ │ ├── MemberControllerTest.php
│ │ │ ├── TeamControllerTest.php
│ │ │ └── WorkspaceControllerTest.php
│ │ └── Observers
│ │ └── CardObserverTest.php
├── TestCase.php
└── Unit
│ └── ExampleTest.php
└── webpack.mix.js
/.docker/.dockerignore:
--------------------------------------------------------------------------------
1 | mongo
2 |
--------------------------------------------------------------------------------
/.docker/bin/apachelinker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PUBLIC_FOLDER=$1
4 |
5 | ln -sfn $1 /var/www/dev
6 | service apache2 start
7 |
--------------------------------------------------------------------------------
/.docker/bin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ./.docker/bin/apachelinker.sh /home/board/public
4 |
5 | chown www-data:$USER bootstrap/cache -R
6 | chown www-data storage -R
7 |
8 | if ! [ -d /tmp/dev.log ]; then
9 | touch /tmp/dev.log
10 | fi
11 |
12 | tail -f /tmp/dev.log
13 |
--------------------------------------------------------------------------------
/.docker/conf/.bashrc:
--------------------------------------------------------------------------------
1 | export TZ=America/Recife
2 |
3 | echo " _ *** ${BASE_IMAGE} ***"
4 | echo " *v* *** ${IMAGE_ALIAS} ***"
5 | echo " /(_)\ "
6 | echo " ^ ^ " `date`
7 |
8 | echo -e ${INFO_IMAGE}
9 |
10 | export PS1="\[$(tput bold)\][\[\e[33m\]${BASE_IMAGE}${SEPARATOR}${IMAGE_ALIAS}\[\e[m\]\[$(tput bold)\]] \[$(tput bold)\]\[\033[38;5;4m\]\w\[$(tput sgr0)\]\[$(tput sgr0)\]\[\033[38;5;15m\]\[$(tput sgr0)\]\$(__git_ps1) # "
11 |
12 | alias ls='ls --color=auto'
13 |
14 | if ! shopt -oq posix; then
15 | if [ -f /usr/share/bash-completion/bash_completion ]; then
16 | . /usr/share/bash-completion/bash_completion
17 | elif [ -f /etc/bash_completion ]; then
18 | . /etc/bash_completion
19 | fi
20 | fi
21 |
--------------------------------------------------------------------------------
/.docker/conf/apache.conf:
--------------------------------------------------------------------------------
1 | # credits: github.com/lissonpsantos2
2 |
3 |
4 | ServerName dev.app
5 | ServerAlias www.dev.app
6 |
7 | ServerAdmin webmaster@localhost
8 | DocumentRoot /var/www/dev
9 |
10 |
11 | Options -Indexes +FollowSymLinks +MultiViews +Includes
12 | AllowOverride AuthConfig
13 | Require all granted
14 |
15 | DirectoryIndex index.php
16 |
17 |
18 |
19 | Options -Indexes +FollowSymLinks +MultiViews +Includes
20 | AllowOverride All
21 | Require all granted
22 | RewriteEngine on
23 | RewriteBase /
24 | RewriteCond %{REQUEST_FILENAME} !-f
25 | RewriteCond %{REQUEST_FILENAME} !-d
26 | RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
27 | RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
28 |
29 |
30 |
31 | ErrorLog /tmp/dev.log
32 | CustomLog /tmp/dev.log combined
33 |
34 |
35 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = tab
8 | indent_size = 2
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Trelássio
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://localhost
6 |
7 | LOG_CHANNEL=daily
8 |
9 | DB_CONNECTION=mongodb
10 | DB_HOST=mongo
11 | DB_PORT=27017
12 | DB_DATABASE=trelassio
13 | DB_USERNAME=
14 | DB_PASSWORD=
15 |
16 | BROADCAST_DRIVER=log
17 | CACHE_DRIVER=file
18 | QUEUE_CONNECTION=sync
19 | SESSION_DRIVER=file
20 | SESSION_LIFETIME=120
21 |
22 | REDIS_HOST=127.0.0.1
23 | REDIS_PASSWORD=null
24 | REDIS_PORT=6379
25 |
26 | MAIL_DRIVER=smtp
27 | MAIL_HOST=smtp.mailtrap.io
28 | MAIL_PORT=2525
29 | MAIL_USERNAME=null
30 | MAIL_PASSWORD=null
31 | MAIL_ENCRYPTION=null
32 |
33 | AWS_ACCESS_KEY_ID=
34 | AWS_SECRET_ACCESS_KEY=
35 | AWS_DEFAULT_REGION=us-east-1
36 | AWS_BUCKET=
37 |
38 | PUSHER_APP_ID=
39 | PUSHER_APP_KEY=
40 | PUSHER_APP_SECRET=
41 | PUSHER_APP_CLUSTER=mt1
42 |
43 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
44 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
45 |
46 | MIX_UTIL_LINKS=
47 |
--------------------------------------------------------------------------------
/.env.testing.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Trelássio
2 | APP_ENV=testing
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://localhost
6 |
7 | LOG_CHANNEL=daily
8 |
9 | DB_CONNECTION=mongodb
10 | DB_HOST=mongo
11 | DB_PORT=27017
12 | DB_DATABASE=trelassio_test
13 | DB_USERNAME=
14 | DB_PASSWORD=
15 |
16 | BROADCAST_DRIVER=log
17 | CACHE_DRIVER=file
18 | QUEUE_CONNECTION=sync
19 | SESSION_DRIVER=file
20 | SESSION_LIFETIME=120
21 |
22 | REDIS_HOST=127.0.0.1
23 | REDIS_PASSWORD=null
24 | REDIS_PORT=6379
25 |
26 | MAIL_DRIVER=smtp
27 | MAIL_HOST=smtp.mailtrap.io
28 | MAIL_PORT=2525
29 | MAIL_USERNAME=null
30 | MAIL_PASSWORD=null
31 | MAIL_ENCRYPTION=null
32 |
33 | AWS_ACCESS_KEY_ID=
34 | AWS_SECRET_ACCESS_KEY=
35 | AWS_DEFAULT_REGION=us-east-1
36 | AWS_BUCKET=
37 |
38 | PUSHER_APP_ID=
39 | PUSHER_APP_KEY=
40 | PUSHER_APP_SECRET=
41 | PUSHER_APP_CLUSTER=mt1
42 |
43 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
44 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
45 |
46 | GITLAB_TOKEN=
47 | GITLAB_PROJECT_ID=
48 | MIX_UTIL_LINKS=
49 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/.eslintignore
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "parser": "babel-eslint",
4 | "ecmaVersion": 2015,
5 | "sourceType": "module"
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:vue/recommended",
10 | "airbnb-base"
11 | ],
12 |
13 | "settings": {
14 | "import/resolver": {
15 | "node": {
16 | "extensions": [
17 | ".js",
18 | ".vue"
19 | ]
20 | }
21 | }
22 | },
23 |
24 | "rules": {
25 | "vue/html-indent": ["error", "tab"],
26 | "indent": ["error", "tab"],
27 | "semi": ["error", "always"],
28 | "eol-last": ["error", "always"],
29 | "no-tabs": "off",
30 | "no-param-reassign": "off",
31 | "camelcase": "off",
32 | "no-else-return": "error"
33 | },
34 |
35 | "globals": {
36 | "localStorage": true,
37 | "d3": true,
38 | "_": true,
39 | "moment": true,
40 | "axios": true,
41 | "swal": true
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-vendored
5 | CHANGELOG.md export-ignore
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/hot
3 | /public/storage
4 | /public/*.js
5 | /public/css
6 | /public/js
7 | /public/mix-manifest.json
8 | /storage/*.key
9 | /vendor
10 | .env
11 | .env.backup
12 | .env.testing
13 | .phpunit.result.cache
14 | Homestead.json
15 | Homestead.yaml
16 | npm-debug.log
17 | yarn-error.log
18 | public/mix-manifest.json
19 | .docker/mongo
20 | .virtualenv
21 | .vscode/
22 | .idea/
23 |
--------------------------------------------------------------------------------
/.rsync-filter:
--------------------------------------------------------------------------------
1 | - .docker
2 | - node_modules
3 | - storage/logs/*
4 | - .git
5 | - .env
6 | - .env.example
7 | - tests
8 | - storage/framework/sessions/*
9 | - storage/framework/cache/*
10 | - production
11 | - public/.htaccess
12 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | php:
2 | preset: laravel
3 | disabled:
4 | - unused_use
5 | finder:
6 | not-name:
7 | - index.php
8 | - server.php
9 | js:
10 | finder:
11 | not-name:
12 | - webpack.mix.js
13 | css: true
14 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Sysvale Health Tech
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 |
--------------------------------------------------------------------------------
/apache.conf:
--------------------------------------------------------------------------------
1 |
2 | ServerName dev.app
3 | ServerAlias www.dev.app
4 |
5 | ServerAdmin webmaster@localhost
6 | DocumentRoot /var/www/dev
7 |
8 |
9 | Options -Indexes +FollowSymLinks +MultiViews +Includes
10 | AllowOverride AuthConfig
11 | Order allow,deny
12 | allow from all
13 |
14 |
15 |
16 | Options -Indexes +FollowSymLinks +MultiViews +Includes
17 | Order allow,deny
18 | allow from all
19 | RewriteEngine on
20 | RewriteBase /
21 | RewriteCond %{REQUEST_FILENAME} !-f
22 | RewriteCond %{REQUEST_FILENAME} !-d
23 | RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
24 | RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
25 |
26 |
27 |
28 | ErrorLog /tmp/dev.log
29 | CustomLog /tmp/dev.log combined
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/Console/Commands/CompanyClientCommand.php:
--------------------------------------------------------------------------------
1 | argument('company_id'));
19 | if (!$company) {
20 | $this->error('Company not found');
21 | return;
22 | }
23 |
24 | $company_client = CompanyClient::firstOrNew(['company_id' => $company->id]);
25 |
26 | if (!$company_client->client) {
27 | try {
28 | $client = $this->createClient();
29 | $company_client->client_id = $client['client_id'];
30 | $company_client->save();
31 | } catch (RuntimeException $exception) {
32 | $this->error($exception->getMessage());
33 | return;
34 | }
35 | }
36 |
37 | $this->line('Client ID: ' . $company_client->client_id);
38 | $this->line('Client Secret: ' . $company_client->refresh()->client->secret);
39 | }
40 |
41 | private function createClient(): array
42 | {
43 | $process = new Process(['php', 'artisan', 'passport:client', '--client']);
44 | $process->run();
45 |
46 | throw_if(!$process->isSuccessful(), RuntimeException::class, $process->getErrorOutput());
47 |
48 | $output = $process->getOutput();
49 |
50 | if (!preg_match('/Client ID: (.+)\nClient secret: (.+)\n/', $output, $matches)) {
51 | throw new RuntimeException('Não foi possível extrair o client_id e o client_secret.');
52 | }
53 |
54 | return [
55 | 'client_id' => $matches[1],
56 | 'client_secret' => $matches[2],
57 | ];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | command('inspire')
28 | // ->hourly();
29 | }
30 |
31 | /**
32 | * Register the commands for the application.
33 | *
34 | * @return void
35 | */
36 | protected function commands()
37 | {
38 | $this->load(__DIR__.'/Commands');
39 |
40 | require base_path('routes/console.php');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Constants/BoardKeys.php:
--------------------------------------------------------------------------------
1 | user = $user;
28 | }
29 |
30 | /**
31 | * Get the channels the event should broadcast on.
32 | *
33 | * @return \Illuminate\Broadcasting\Channel|array
34 | */
35 | public function broadcastOn()
36 | {
37 | return new PrivateChannel('channel-name');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 | middleware('auth');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/ForgotPasswordController.php:
--------------------------------------------------------------------------------
1 | middleware('guest')->except('logout');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/ResetPasswordController.php:
--------------------------------------------------------------------------------
1 | configured_password = true;
35 | $user->password = Hash::make($password);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/VerificationController.php:
--------------------------------------------------------------------------------
1 | middleware('auth');
39 | $this->middleware('signed')->only('verify');
40 | $this->middleware('throttle:6,1')->only('verify', 'resend');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Http/Controllers/BacklogLabelController.php:
--------------------------------------------------------------------------------
1 | get();
16 | }
17 |
18 | public function store(Request $request)
19 | {
20 | $data = $request->validate([
21 | 'name' => 'required|string',
22 | 'text_color' => 'required|string',
23 | 'color' => 'required|string',
24 | ]);
25 |
26 | $backlog_label = BacklogLabel::create($data);
27 |
28 | return new BacklogLabelResource($backlog_label);
29 | }
30 |
31 | public function update(Request $request, BacklogLabel $backlog_label)
32 | {
33 | $data = $request->validate([
34 | 'name' => 'required|string',
35 | 'text_color' => 'required|string',
36 | 'color' => 'required|string',
37 | ]);
38 |
39 | $backlog_label->update($data);
40 |
41 | return new BacklogLabelResource($backlog_label);
42 | }
43 |
44 | public function destroy(BacklogLabel $backlog_label)
45 | {
46 | $backlog_label->delete();
47 |
48 | return Response::json('Categoria excluída com sucesso!');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/Http/Controllers/BoardController.php:
--------------------------------------------------------------------------------
1 | getTaskLists($in->team_id);
17 | }
18 |
19 | public function getDevlogLists(Request $in)
20 | {
21 | return (new BoardListService())->getDevLists($in->team_id);
22 |
23 | }
24 |
25 | public function getPlanningLists(Workspace $workspace)
26 | {
27 | return (new BoardListService())->getPlanningLists($workspace->id);
28 | }
29 |
30 | public function getCompanyPlanningLists()
31 | {
32 | return (new BoardListService())->getCompanyPlanningLists();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | id)
14 | ->get()
15 | ->sortByDesc('created_at')
16 | ->values();
17 |
18 | return $impediments;
19 | }
20 |
21 | public function store(Request $in)
22 | {
23 | $event = Event::create([
24 | 'name' => $in->name,
25 | 'members' => $in->members,
26 | 'team_id' => $in->team_id,
27 | 'labels' => $in->labels,
28 | 'start' => $in->start,
29 | 'end' => $in->end,
30 | ]);
31 |
32 | return $event;
33 | }
34 |
35 | public function update(Request $in, $id)
36 | {
37 | $params = [
38 | 'name' => $in->name,
39 | 'labels' => $in->labels,
40 | 'members' => $in->members,
41 | 'team_id' => $in->team_id ?? $in->team['id'],
42 | 'start' => $in->start,
43 | 'end' => $in->end,
44 | ];
45 |
46 | $event = Event::where('_id', $id);
47 | $event->update($params);
48 |
49 | return $event->get()->first();
50 | }
51 |
52 | public function destroy($id)
53 | {
54 | $event = Event::where('_id', $id)
55 | ->delete();
56 | return $event;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/Http/Controllers/GoalController.php:
--------------------------------------------------------------------------------
1 | $in->title,
19 | 'workspace_id' => $in->workspace_id,
20 | 'team_id' => $in->team_id,
21 | ]);
22 |
23 | return $goal;
24 | }
25 |
26 | public function update(Request $in, $id)
27 | {
28 | $params = [
29 | 'title' => $in->title,
30 | 'workspace_id' => $in->workspace_id,
31 | 'team_id' => $in->team_id,
32 | ];
33 |
34 | $goal = Goal::where('_id', $id);
35 | $goal->update($params);
36 |
37 | return $goal->get()->first();
38 | }
39 |
40 | public function destroy($id)
41 | {
42 | $goal = Goal::where('_id', $id)
43 | ->delete();
44 | return $goal;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/Http/Controllers/LabelController.php:
--------------------------------------------------------------------------------
1 | get();
16 | }
17 |
18 | public function store(Request $request)
19 | {
20 | $data = $request->validate([
21 | 'name' => 'required|string',
22 | 'text_color' => 'required|string',
23 | 'color' => 'required|string',
24 | 'workspace_id' => 'required|string',
25 | ]);
26 |
27 | $label = Label::create($data);
28 |
29 | return new LabelResource($label);
30 | }
31 |
32 | public function update(Request $request, Label $label)
33 | {
34 | $data = $request->validate([
35 | 'name' => 'required|string',
36 | 'text_color' => 'required|string',
37 | 'color' => 'required|string',
38 | 'workspace_id' => 'required|string',
39 | ]);
40 |
41 | $label->update($data);
42 |
43 | return new LabelResource($label);
44 | }
45 |
46 | public function destroy(Label $label)
47 | {
48 | $label->delete();
49 |
50 | return Response::json('Categoria excluída com sucesso!');
51 | }
52 |
53 | public function getLabelsByWorkspaceId(Workspace $workspace)
54 | {
55 | return Label::where('workspace_id', $workspace->id)->orderBy('name')->get();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/Http/Controllers/ProcessController.php:
--------------------------------------------------------------------------------
1 | validate([
27 | 'name' => 'required|string',
28 | 'team_ids' => 'required|array',
29 | 'checklists' => 'required|array',
30 | ]);
31 |
32 | $process = Process::create($data);
33 |
34 | $process->company()->associate(
35 | auth()->user()->member->company_id
36 | );
37 |
38 | $process->save();
39 |
40 | return new ProcessResource($process);
41 | }
42 |
43 | public function update(Request $request, Process $process)
44 | {
45 | $data = $request->validate([
46 | 'name' => 'required|string',
47 | 'team_ids' => 'required|array',
48 | 'checklists' => 'required|array',
49 | ]);
50 |
51 | $process->update($data);
52 |
53 | return new ProcessResource($process);
54 | }
55 |
56 | public function destroy(Process $process)
57 | {
58 | $process->delete();
59 |
60 | return Response::json('Deletado com sucesso.', 200);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/Http/Controllers/UserController.php:
--------------------------------------------------------------------------------
1 | validate([
16 | 'user_id' => 'required|string',
17 | ]);
18 |
19 | $user = User::findOrFail($request->user_id);
20 | Mail::to($user->email)->send(new WelcomeMail($user));
21 |
22 | return Response::json('E-mail enviado com sucesso!');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | expectsJson()) {
18 | return route('login');
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Http/Middleware/CheckForMaintenanceMode.php:
--------------------------------------------------------------------------------
1 | company_id)) {
25 | $company_scope = new CompanyScope($request->company_id);
26 | Card::addGlobalScope($company_scope);
27 | Member::addGlobalScope($company_scope);
28 | Team::addGlobalScope($company_scope);
29 | Workspace::addGlobalScope($company_scope);
30 | Process::addGlobalScope($company_scope);
31 | }
32 |
33 | return $next($request);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Http/Middleware/EncryptCookies.php:
--------------------------------------------------------------------------------
1 | check()) {
22 | return redirect(RouteServiceProvider::HOME);
23 | }
24 |
25 | return $next($request);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Http/Middleware/ResolveCompanyTenant.php:
--------------------------------------------------------------------------------
1 | parser = $parser;
21 | $this->tokenRepository = $tokenRepository;
22 | }
23 |
24 | public function handle(Request $request, Closure $next)
25 | {
26 | if(Auth::check()) {
27 | $company_id = auth()->user()->member->company_id;
28 | }
29 |
30 | if (!empty($request->bearerToken())) {
31 | $bearerToken = request()->bearerToken();
32 | $tokenId = $this->parser->parse($bearerToken)->claims()->get('jti');
33 | $token = $this->tokenRepository->find($tokenId);
34 | $company_id = CompanyClient::where('client_id', $token->client_id)->first()->company_id;
35 | }
36 |
37 | $request->merge(['company_id' => $company_id ?? null]);
38 |
39 | return $next($request);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrimStrings.php:
--------------------------------------------------------------------------------
1 | globalRules(),
19 | $this->getRulesByType()
20 | );
21 | }
22 |
23 | protected function globalRules(): array
24 | {
25 | $types = CardTypes::allJoin();
26 |
27 | return [
28 | 'title' => 'required|string',
29 | 'type' => "required|in:$types",
30 | 'board_list_id' => 'required|string|exists:board_lists,_id',
31 | 'user_story_id' => 'nullable|string',
32 | 'members' => 'nullable|array',
33 | 'team_id' => 'nullable|string',
34 | 'board_id' => 'nullable|string',
35 | 'labels' => 'nullable|string',
36 | 'link' => 'nullable|string',
37 | 'workspace_id' => 'nullable|string',
38 | 'checklist' => 'nullable|array',
39 | ];
40 | }
41 |
42 | protected function getRulesByType(): array
43 | {
44 | if ($this->type === CardTypes::NOT_PRIORITIZED) {
45 | return $this->notPrioritizedRules();
46 | }
47 |
48 | if ($this->type === CardTypes::USER_STORY) {
49 | return $this->userStoryRules();
50 | }
51 |
52 | return [];
53 | }
54 |
55 | protected function notPrioritizedRules(): array
56 | {
57 | return [
58 | 'rating' => 'nullable|numeric|min:0|max:5',
59 | 'description' => 'nullable|string',
60 | 'backlog_labels' => 'nullable|string',
61 | ];
62 | }
63 |
64 | protected function userStoryRules(): array
65 | {
66 | return [
67 | 'bimester_goal' => 'nullable|boolean',
68 | 'is_recurrent' => 'nullable|boolean',
69 | 'backlog_labels' => 'nullable|string',
70 | 'milestone_id' => 'nullable|string',
71 | ];
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/Http/Resources/BacklogLabelResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'text_color' => $this->text_color,
21 | 'color' => $this->color,
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Resources/LabelResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'text_color' => $this->text_color,
21 | 'color' => $this->color,
22 | 'workspace_id' => $this->workspace_id,
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/Http/Resources/MemberResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'email' => $this->email,
21 | 'full_email' => $this->full_email,
22 | 'has_login' => $this->has_login,
23 | 'user_id' => optional($this->user)->id,
24 | 'avatar_url' => $this->avatar_url,
25 | 'teams' => implode(', ', $this->getTeams()->pluck('name')->toArray()),
26 | 'team_ids' => $this->team_ids,
27 | 'workspace_ids' => $this->workspace_ids,
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Http/Resources/MilestoneResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'title' => $this->title,
20 | 'description' => $this->description,
21 | 'start_date' => $this->start_date,
22 | 'end_date' => $this->end_date,
23 | 'team_ids' => $this->when(isset($this->team_ids), $this->team_ids),
24 | 'acceptance_criteria' => $this->when(isset($this->acceptance_criteria), $this->acceptance_criteria),
25 | 'status' => $this->status,
26 | 'closed' => $this->closed,
27 | ];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Resources/ProcessResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'team_ids' => $this->team_ids,
21 | 'checklists' => (array) $this->checklists,
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Http/Resources/SprintReportResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'started_at' => $this->started_at,
20 | 'finished_at' => $this->finished_at,
21 | 'members' => $this->members,
22 | 'notes' => $this->notes,
23 | 'impediments_quantity' => $this->impediments_quantity,
24 | 'cards' => $this->cards,
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Http/Resources/TeamResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'extended_task_flow' => $this->extended_task_flow,
21 | 'key' => $this->key,
22 | 'workspace_id' => $this->workspace_id,
23 | 'board_lists' => $this->board_lists,
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Resources/WorkspaceResource.php:
--------------------------------------------------------------------------------
1 | $this->id,
19 | 'name' => $this->name,
20 | 'team_ids' => $this->team_ids,
21 | 'label_ids' => $this->label_ids,
22 | 'team_names' => $this->team_names,
23 | 'lottie_file' => $this->lottie_file,
24 | 'settings' => (object) $this->settings,
25 | 'inactive' => $this->inactive ?? false,
26 | 'status' => $this->inactive ? 'Desativado' : '',
27 | ];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Library/Utils/BagOfConstants.php:
--------------------------------------------------------------------------------
1 | getConstants());
12 |
13 | return array_filter($constant_values, function ($value) {
14 | return !is_array($value) && !is_object($value);
15 | });
16 | }
17 |
18 | public static function allJoin()
19 | {
20 | return implode(',', static::all());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Listeners/SendWelcomingEmail.php:
--------------------------------------------------------------------------------
1 | user->email)->send(new WelcomeMail($event->user));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Mail/WelcomeMail.php:
--------------------------------------------------------------------------------
1 | user = $user;
26 | }
27 |
28 | /**
29 | * Build the message.
30 |
31 | * @return $this
32 | */
33 | public function build()
34 | {
35 | $first_name = explode(' ', $this->user->name)[0];
36 |
37 | $token = app('auth.password.broker')->createToken($this->user);
38 |
39 | $url = route('password.reset', [
40 | 'token' => $token,
41 | 'email' => $this->user->email,
42 | ]);
43 |
44 | return $this
45 | ->subject('Bem vindo ao Trelássio!')
46 | ->markdown('vendor.emails.welcome', [
47 | 'url' => $url,
48 | 'name' => $first_name
49 | ]);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Models/BacklogLabel.php:
--------------------------------------------------------------------------------
1 | hasMany('App\Models\BoardList');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Models/BoardList.php:
--------------------------------------------------------------------------------
1 | orderBy('position');
33 | });
34 | }
35 |
36 | public function cards()
37 | {
38 | return $this->hasMany('App\Models\Card');
39 | }
40 |
41 | public function team()
42 | {
43 | return $this->belongsTo('App\Models\Team');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Models/Company.php:
--------------------------------------------------------------------------------
1 | hasMany(User::class);
30 | }
31 |
32 | public function workspace()
33 | {
34 | return $this->hasMany(Workspace::class);
35 | }
36 |
37 | public function teams()
38 | {
39 | return $this->hasMany(Team::class);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Models/CompanyClient.php:
--------------------------------------------------------------------------------
1 | belongsTo(Company::class);
22 | }
23 |
24 | public function client(): BelongsTo
25 | {
26 | return $this->belongsTo(Client::class);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Models/Event.php:
--------------------------------------------------------------------------------
1 | belongsTo('App\Models\Team');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Models/Goal.php:
--------------------------------------------------------------------------------
1 | belongsTo('App\Models\Team');
23 | }
24 |
25 | public function workspace()
26 | {
27 | return $this->belongsTo('App\Models\Workspace');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Models/Label.php:
--------------------------------------------------------------------------------
1 | belongsTo(Workspace::class);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/Models/Member.php:
--------------------------------------------------------------------------------
1 | hasOne(User::class, 'member_id');
26 | }
27 |
28 | public function company()
29 | {
30 | return $this->belongsTo(Company::class);
31 | }
32 |
33 | public function getFullEmailAttribute()
34 | {
35 | return optional($this->user)->email ?? '';
36 | }
37 |
38 | public function getEmailAttribute()
39 | {
40 | return $this->full_email;
41 | }
42 |
43 | public function getHasLoginAttribute()
44 | {
45 | return !!optional($this->user)->configured_password;
46 | }
47 |
48 | public function teamMembers()
49 | {
50 | return $this->hasMany(TeamMember::class);
51 | }
52 |
53 | public function getTeams()
54 | {
55 | return $this->teamMembers->map(function ($pivot) {
56 | return $pivot->team;
57 | });
58 | }
59 |
60 | public function getTeamIdsAttribute()
61 | {
62 | return $this->getTeams()->pluck('id')->toArray();
63 | }
64 |
65 | public function getWorkspaceIdsAttribute()
66 | {
67 | return $this->getTeams()->pluck('workspace_id')->toArray();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/Models/Milestone.php:
--------------------------------------------------------------------------------
1 | hasMany(Team::class);
26 | }
27 |
28 | public function company()
29 | {
30 | return $this->belongsTo(Company::class);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/Models/Scopes/CompanyScope.php:
--------------------------------------------------------------------------------
1 | company_id = $company_id;
16 | }
17 |
18 | public function apply(Builder $builder, Model $model): void
19 | {
20 | if ($this->company_id) {
21 | $builder->where('company_id', $this->company_id);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/Models/SprintReport.php:
--------------------------------------------------------------------------------
1 | belongsTo(Team::class);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Models/Team.php:
--------------------------------------------------------------------------------
1 | hasMany(Card::class);
29 | }
30 |
31 | public function workspace()
32 | {
33 | return $this->belongsTo(Workspace::class);
34 | }
35 |
36 | public function company()
37 | {
38 | return $this->belongsTo(Company::class);
39 | }
40 |
41 | public function board_lists()
42 | {
43 | return $this->hasMany(BoardList::class);
44 | }
45 |
46 | public function syncBoardLists($from_request)
47 | {
48 |
49 | $current = $this->board_lists;
50 |
51 | $add = array_filter($from_request, function ($item) {
52 | return empty($item['id']);
53 | });
54 |
55 | $update = array_filter($from_request, function ($item) {
56 | return !empty($item['id']);
57 | });
58 |
59 | $remove = array_diff(
60 | $current->pluck('id')->toArray(),
61 | collect($from_request)->pluck('id')->toArray()
62 | );
63 |
64 |
65 | if (empty($add) && empty($remove) && empty($update)) {
66 | return;
67 | }
68 |
69 | $board_list_service = new BoardListService();
70 | $board_list_service->createMany($add, $this->id);
71 | $board_list_service->deleteMany($remove);
72 | $board_list_service->updateMany($update);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/Models/TeamMember.php:
--------------------------------------------------------------------------------
1 | belongsTo(Team::class);
22 | }
23 |
24 | public function member()
25 | {
26 | return $this->belongsTo(Member::class);
27 | }
28 |
29 | public function company()
30 | {
31 | return $this->belongsTo(Company::class);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/Observers/CardObserver.php:
--------------------------------------------------------------------------------
1 | filled('company_id')) {
13 | $card->company()->associate(request()->company_id);
14 | $card->user_id = auth()->check() ? Auth::user()->id : null;
15 | }
16 |
17 | $card->number = (Card::withTrashed()->max('number') ?: 0) + 1;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Observers/TeamObserver.php:
--------------------------------------------------------------------------------
1 | key = $team->id;
22 | $team->save();
23 |
24 | BoardList::create([
25 | 'name' => 'Sprint - '. $team->name,
26 | 'key' => $team->id,
27 | 'user_story_holder' => true,
28 | 'accepts_card_type' => CardTypes::USER_STORY,
29 | 'is_goalable' => true,
30 | 'position' => 3,
31 | ]);
32 |
33 | Goal::create([
34 | 'title' => 'Defina um objetivo',
35 | 'team_id' => $team->id,
36 | ]);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(FakerGenerator::class, function () {
24 | return FakerFactory::create('pt_BR');
25 | });
26 | }
27 |
28 | /**
29 | * Bootstrap any application services.
30 | *
31 | * @return void
32 | */
33 | public function boot()
34 | {
35 | JsonResource::withoutWrapping();
36 | Card::observe(CardObserver::class);
37 | Team::observe(TeamObserver::class);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | 'App\Policies\ModelPolicy',
18 | ];
19 |
20 | /**
21 | * Register any authentication / authorization services.
22 | *
23 | * @return void
24 | */
25 | public function boot()
26 | {
27 | $this->registerPolicies();
28 | Passport::routes();
29 | //
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Providers/BroadcastServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
23 | SendEmailVerificationNotification::class,
24 | ],
25 | 'App\Events\UserRegisteredEvent' => [
26 | 'App\Listeners\SendWelcomingEmail',
27 | ],
28 | ];
29 |
30 | /**
31 | * Register any events for your application.
32 | *
33 | * @return void
34 | */
35 | public function boot()
36 | {
37 | parent::boot();
38 | //
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | mapApiRoutes();
46 |
47 | $this->mapWebRoutes();
48 |
49 | //
50 | }
51 |
52 | /**
53 | * Define the "web" routes for the application.
54 | *
55 | * These routes all receive session state, CSRF protection, etc.
56 | *
57 | * @return void
58 | */
59 | protected function mapWebRoutes()
60 | {
61 | Route::middleware('web')
62 | ->namespace($this->namespace)
63 | ->group(base_path('routes/web.php'));
64 | }
65 |
66 | /**
67 | * Define the "api" routes for the application.
68 | *
69 | * These routes are typically stateless.
70 | *
71 | * @return void
72 | */
73 | protected function mapApiRoutes()
74 | {
75 | Route::prefix('api')
76 | ->middleware('api')
77 | ->namespace($this->namespace)
78 | ->group(base_path('routes/api.php'));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/Services/CardService.php:
--------------------------------------------------------------------------------
1 | first()->id;
20 |
21 | $cards = Card::where('board_list_id', $sprintListId)
22 | ->where('type', CardTypes::USER_STORY)
23 | ->orderBy('position')
24 | ->get();
25 |
26 | return CardResource::collection($cards);
27 | }
28 |
29 | public function getTasksFromUserStory($user_story_id, $team_board_lists)
30 | {
31 | $cards = Card::whereIn('board_list_id', $team_board_lists)
32 | ->where('user_story_id', $user_story_id)
33 | ->orderBy('position')
34 | ->get();
35 |
36 | return CardResource::collection($cards)->groupBy('board_list_id');
37 | }
38 |
39 | public function getTasksFromBoard($board_id, $team_board_lists)
40 | {
41 | $cards = Card::whereIn('board_list_id', $team_board_lists)
42 | ->where('board_id', $board_id)
43 | ->orderBy('position')
44 | ->get();
45 |
46 | return CardResource::collection($cards)->groupBy('board_list_id');
47 | }
48 |
49 | public function getCardsByBoardListsIds($team_board_lists)
50 | {
51 | $cards = Card::whereIn('board_list_id', $team_board_lists)
52 | ->orderBy('position')
53 | ->get();
54 |
55 | return CardResource::collection($cards)->groupBy('board_list_id');
56 | }
57 |
58 | public function deleteMany($cards_ids)
59 | {
60 | if(empty($cards_ids)) return;
61 |
62 | foreach ($cards_ids as $id) {
63 | $find = Card::find($id);
64 | if($find) {
65 | $find->delete();
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/Services/WorkspaceService.php:
--------------------------------------------------------------------------------
1 | orWhereNull('inactive')
18 | ->get();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/User.php:
--------------------------------------------------------------------------------
1 | UserRegisteredEvent::class,
18 | ];
19 |
20 | /**
21 | * The attributes that are mass assignable.
22 | *
23 | * @var array
24 | */
25 | protected $fillable = [
26 | 'name',
27 | 'email',
28 | 'password',
29 | 'member_id',
30 | 'configured_password',
31 | ];
32 |
33 | /**
34 | * The attributes that should be hidden for arrays.
35 | *
36 | * @var array
37 | */
38 | protected $hidden = [
39 | 'password', 'remember_token',
40 | ];
41 |
42 | /**
43 | * The attributes that should be cast to native types.
44 | *
45 | * @var array
46 | */
47 | protected $casts = [
48 | 'email_verified_at' => 'datetime',
49 | ];
50 |
51 | public function member()
52 | {
53 | return $this->belongsTo(Member::class);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Http\Kernel::class,
31 | App\Http\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Console\Kernel::class,
36 | App\Console\Kernel::class
37 | );
38 |
39 | $app->singleton(
40 | Illuminate\Contracts\Debug\ExceptionHandler::class,
41 | App\Exceptions\Handler::class
42 | );
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Return The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | This script returns the application instance. The instance is given to
50 | | the calling script so we can separate the building of the instances
51 | | from the actual running of the application and sending responses.
52 | |
53 | */
54 |
55 | return $app;
56 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/config/broadcasting.php:
--------------------------------------------------------------------------------
1 | env('BROADCAST_DRIVER', 'null'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Broadcast Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the broadcast connections that will be used
26 | | to broadcast events to other systems or over websockets. Samples of
27 | | each available type of connection are provided inside this array.
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'pusher' => [
34 | 'driver' => 'pusher',
35 | 'key' => env('PUSHER_APP_KEY'),
36 | 'secret' => env('PUSHER_APP_SECRET'),
37 | 'app_id' => env('PUSHER_APP_ID'),
38 | 'options' => [
39 | 'cluster' => env('PUSHER_APP_CLUSTER'),
40 | 'useTLS' => true,
41 | ],
42 | ],
43 |
44 | 'redis' => [
45 | 'driver' => 'redis',
46 | 'connection' => 'default',
47 | ],
48 |
49 | 'log' => [
50 | 'driver' => 'log',
51 | ],
52 |
53 | 'null' => [
54 | 'driver' => 'null',
55 | ],
56 |
57 | ],
58 |
59 | ];
60 |
--------------------------------------------------------------------------------
/config/hashing.php:
--------------------------------------------------------------------------------
1 | 'bcrypt',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Bcrypt Options
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify the configuration options that should be used when
26 | | passwords are hashed using the Bcrypt algorithm. This will allow you
27 | | to control the amount of time it takes to hash the given password.
28 | |
29 | */
30 |
31 | 'bcrypt' => [
32 | 'rounds' => env('BCRYPT_ROUNDS', 10),
33 | ],
34 |
35 | /*
36 | |--------------------------------------------------------------------------
37 | | Argon Options
38 | |--------------------------------------------------------------------------
39 | |
40 | | Here you may specify the configuration options that should be used when
41 | | passwords are hashed using the Argon algorithm. These will allow you
42 | | to control the amount of time it takes to hash the given password.
43 | |
44 | */
45 |
46 | 'argon' => [
47 | 'memory' => 1024,
48 | 'threads' => 2,
49 | 'time' => 2,
50 | ],
51 |
52 | ];
53 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'domain' => env('MAILGUN_DOMAIN'),
19 | 'secret' => env('MAILGUN_SECRET'),
20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
21 | ],
22 |
23 | 'postmark' => [
24 | 'token' => env('POSTMARK_TOKEN'),
25 | ],
26 |
27 | 'ses' => [
28 | 'key' => env('AWS_ACCESS_KEY_ID'),
29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
31 | ],
32 |
33 | 'sparkpost' => [
34 | 'secret' => env('SPARKPOST_SECRET'),
35 | ],
36 |
37 | ];
38 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
17 | resource_path('views'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Compiled View Path
23 | |--------------------------------------------------------------------------
24 | |
25 | | This option determines where all the compiled Blade templates will be
26 | | stored for your application. Typically, this is within the storage
27 | | directory. However, as usual, you are free to change this value.
28 | |
29 | */
30 |
31 | 'compiled' => env(
32 | 'VIEW_COMPILED_PATH',
33 | realpath(storage_path('framework/views'))
34 | ),
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite
2 | *.sqlite-journal
3 |
--------------------------------------------------------------------------------
/database/factories/BoardListFactory.php:
--------------------------------------------------------------------------------
1 | define(BoardList::class, function (Faker $faker) {
9 | return [
10 | 'name' => $faker->name,
11 | 'position' => $faker->numberBetween(1, 10),
12 | 'key' => $faker->md5,
13 | 'user_story_holder' => $faker->name,
14 | 'accepts_card_type' => [],
15 | 'team_id' => $faker->md5,
16 | 'is_devlog' => $faker->boolean(50),
17 | 'is_goalable' => $faker->boolean(50),
18 | ];
19 | });
20 |
--------------------------------------------------------------------------------
/database/factories/CardFactory.php:
--------------------------------------------------------------------------------
1 | define(Card::class, function (Faker $faker) {
10 | return [
11 | 'number' => $faker->numberBetween(1, 100),
12 | 'title' => $faker->sentence(),
13 | 'link' => $faker->url(),
14 | 'position' => $faker->numberBetween(1, 100),
15 | 'type' => 'user-story',
16 | ];
17 | });
18 |
19 | $factory->state(Card::class, 'with-board', function () {
20 | return [
21 | 'board_list_id' => factory(BoardList::class)
22 | ->create()
23 | ->id,
24 | ];
25 | });
26 |
--------------------------------------------------------------------------------
/database/factories/CompanyFactory.php:
--------------------------------------------------------------------------------
1 | define(Company::class, function (Faker $faker) {
9 | return [
10 | 'cnpj' => $faker->regexify('[0-9]{15}'),
11 | 'name' => $faker->name(),
12 | 'phone' => $faker->phoneNumber(),
13 | 'email' => $faker->email(),
14 | ];
15 | });
16 |
--------------------------------------------------------------------------------
/database/factories/MemberFactory.php:
--------------------------------------------------------------------------------
1 | define(Member::class, function (Faker $faker) {
10 | return [
11 | 'name' => $faker->name(),
12 | 'avatar_url' => $faker->url(),
13 | ];
14 | });
15 |
16 | $factory->state(Member::class, 'with-company', function () {
17 | return [
18 | 'company_id' => factory(Company::class)->create()->id,
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/factories/TeamFactory.php:
--------------------------------------------------------------------------------
1 | define(Team::class, function (Faker $faker) {
10 | return [
11 | 'name' => $faker->name(),
12 | 'board_lists' => [1,2],
13 | 'board_lists.0.name' => $faker->name(),
14 | 'board_lists.1.name' => $faker->name(),
15 | 'board_lists.0.position' => $faker->numberBetween(1,9),
16 | 'board_lists.1.position' => $faker->numberBetween(1,9),
17 | 'lottie_file' => $faker->url(),
18 | ];
19 | });
20 |
21 | $factory->state(Team::class, 'with-company', function() {
22 | return [
23 | 'company_id' => factory(Company::class)->create()->id,
24 | ];
25 | });
26 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | define(User::class, function (Faker $faker) {
23 | return [
24 | 'name' => $faker->name,
25 | 'email' => $faker->unique()->safeEmail,
26 | 'email_verified_at' => now(),
27 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
28 | 'remember_token' => Str::random(10),
29 | 'active' => true,
30 | 'type' => 'A',
31 | ];
32 | });
33 |
34 | $factory->state(User::class, 'with-member', function () {
35 | return [
36 | 'member_id' => factory(Member::class)
37 | ->create()
38 | ->id,
39 | ];
40 | });
41 |
42 | $factory->state(User::class, 'with-member-company', function () {
43 | return [
44 | 'member_id' => factory(Member::class)
45 | ->state('with-company')
46 | ->create()
47 | ->id,
48 | ];
49 | });
50 |
--------------------------------------------------------------------------------
/database/factories/WorkspaceFactory.php:
--------------------------------------------------------------------------------
1 | define(Workspace::class, function (Faker $faker) {
10 | return [
11 | 'name' => $faker->name(),
12 | 'lottie_file' => $faker->url(),
13 | ];
14 | });
15 |
16 | $factory->state(Workspace::class, 'with-company', function() {
17 | return [
18 | 'company_id' => factory(Company::class)->create()->id,
19 | ];
20 | });
21 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestamp('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('users');
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_100000_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
18 | $table->string('token');
19 | $table->timestamp('created_at')->nullable();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::dropIfExists('password_resets');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call([]);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | board-server:
5 | image: elaynelemos/ubuntu-20.04-laravel-6.x:node-12.x-php-7.4-apache-2
6 | volumes:
7 | - ./:/home/board/
8 | ports:
9 | - 8090:80
10 | stdin_open: true
11 | tty: true
12 | entrypoint:
13 | - ".docker/bin/entrypoint.sh"
14 | - bash
15 | working_dir: /home/board/
16 | mem_limit: 2g
17 | depends_on:
18 | - mongo
19 |
20 | mongo:
21 | image: mongo:4.2
22 | volumes:
23 | - './.docker/mongo/data:/data/db:z'
24 | ports:
25 | - '27003:27017'
26 |
27 | volumes:
28 | mongodb_data:
29 | driver: local
30 | server_data:
31 | driver: local
32 |
--------------------------------------------------------------------------------
/init-infra.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | EXIT_FAILURE=1
6 | EXIT_SUCCESS=0
7 |
8 | virtual_env="${APPLICATION_IMAGES_VIRTUAL_ENV:-.virtualenv}"
9 |
10 | pip3 install virtualenv
11 | virtualenv -p python3 $virtual_env
12 | source $virtual_env/bin/activate
13 | pip install --upgrade pip
14 | pip install -r requirements.txt
15 |
16 | exit $EXIT_SUCCESS
17 |
--------------------------------------------------------------------------------
/mongo_indexs.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/mongo_indexs.js
--------------------------------------------------------------------------------
/on-server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker-compose exec board-server $*
4 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 | A custom coding standard
10 |
11 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 0
36 |
37 |
38 |
39 | */database/*
40 |
41 |
42 | vendor
43 |
44 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests/Unit
14 |
15 |
16 |
17 | ./tests/Feature
18 |
19 |
20 |
21 |
22 | ./app
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Handle Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/christmas-divider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/public/images/christmas-divider.png
--------------------------------------------------------------------------------
/public/images/elofy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/public/images/elofy.png
--------------------------------------------------------------------------------
/public/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/public/images/favicon.ico
--------------------------------------------------------------------------------
/public/images/logo-natal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sysvale/board/8cad9124064683250de0dcc312a1a296d29e8cdc/public/images/logo-natal.png
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | define('LARAVEL_START', microtime(true));
11 |
12 | /*
13 | |--------------------------------------------------------------------------
14 | | Register The Auto Loader
15 | |--------------------------------------------------------------------------
16 | |
17 | | Composer provides a convenient, automatically generated class loader for
18 | | our application. We just need to utilize it! We'll simply require it
19 | | into the script here so that we don't have to worry about manual
20 | | loading any of our classes later on. It feels great to relax.
21 | |
22 | */
23 |
24 | require __DIR__.'/../vendor/autoload.php';
25 |
26 | /*
27 | |--------------------------------------------------------------------------
28 | | Turn On The Lights
29 | |--------------------------------------------------------------------------
30 | |
31 | | We need to illuminate PHP development, so let us turn on the lights.
32 | | This bootstraps the framework and gets it ready for use, then it
33 | | will load up this application so that we can run it and send
34 | | the responses back to the browser and delight our users.
35 | |
36 | */
37 |
38 | $app = require_once __DIR__.'/../bootstrap/app.php';
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Run The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once we have the application, we can handle the incoming request
46 | | through the kernel, and send the associated response back to
47 | | the client's browser allowing them to enjoy the creative
48 | | and wonderful application we have prepared for them.
49 | |
50 | */
51 |
52 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
53 |
54 | $response = $kernel->handle(
55 | $request = Illuminate\Http\Request::capture()
56 | );
57 |
58 | $response->send();
59 |
60 | $kernel->terminate($request, $response);
61 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | ansible==2.9.13
2 | ansible-lint==4.3.5
3 | arrow==0.16.0
4 | bcrypt==3.2.0
5 | binaryornot==0.4.4
6 | Cerberus==1.3.2
7 | certifi==2020.6.20
8 | cffi==1.14.3
9 | chardet==3.0.4
10 | click==7.1.2
11 | click-completion==0.5.2
12 | click-help-colors==0.8
13 | colorama==0.4.3
14 | commonmark==0.9.1
15 | cookiecutter==1.7.2
16 | cryptography==3.1
17 | distro==1.5.0
18 | docker==4.3.1
19 | fasteners==0.15
20 | idna==2.10
21 | Jinja2==2.11.2
22 | jinja2-time==0.2.0
23 | MarkupSafe==1.1.1
24 | molecule==3.0.8
25 | monotonic==1.5
26 | paramiko==2.7.2
27 | pathspec==0.8.0
28 | pexpect==4.8.0
29 | pluggy==0.13.1
30 | poyo==0.5.0
31 | ptyprocess==0.6.0
32 | pycparser==2.20
33 | Pygments==2.7.1
34 | PyNaCl==1.4.0
35 | python-dateutil==2.8.1
36 | python-gilt==1.2.3
37 | python-slugify==4.0.1
38 | PyYAML==5.3.1
39 | requests==2.24.0
40 | rich==7.0.0
41 | ruamel.yaml==0.16.12
42 | ruamel.yaml.clib==0.2.2
43 | selinux==0.2.1
44 | sh==1.13.1
45 | shellingham==1.3.2
46 | six==1.15.0
47 | tabulate==0.8.7
48 | text-unidecode==1.3
49 | tree-format==0.1.2
50 | typing-extensions==3.7.4.3
51 | urllib3==1.25.10
52 | websocket-client==0.57.0
53 | yamllint==1.24.2
54 |
--------------------------------------------------------------------------------
/resources/js/core/constants/milestoneStatuses.js:
--------------------------------------------------------------------------------
1 | export const dictionary = {
2 | INCOMPLETE: 'incomplete',
3 | IN_PROGRESS: 'in-progress',
4 | DONE: 'done',
5 | CANCELED: 'canceled',
6 | NOT_STARTED: 'not-started',
7 | };
8 |
9 | export const options = [
10 | {
11 | text: 'Em andamento',
12 | value: dictionary.IN_PROGRESS,
13 | },
14 | {
15 | text: 'Concluído',
16 | value: dictionary.DONE,
17 | },
18 | {
19 | text: 'Incompleto',
20 | value: dictionary.INCOMPLETE,
21 | },
22 | {
23 | text: 'Cancelado',
24 | value: dictionary.CANCELED,
25 | },
26 | {
27 | text: 'Não iniciado',
28 | value: dictionary.NOT_STARTED,
29 | },
30 | ];
31 |
--------------------------------------------------------------------------------
/resources/js/core/constants/userStoryStatuses.js:
--------------------------------------------------------------------------------
1 | export const dictionary = {
2 | IN_PROGRESS: 'in-progress',
3 | DONE: 'done',
4 | CANCELED: 'canceled',
5 | NOT_STARTED: 'not-started',
6 | };
7 |
8 | export const options = [
9 | {
10 | text: 'Em andamento',
11 | value: dictionary.IN_PROGRESS,
12 | },
13 | {
14 | text: 'Feito',
15 | value: dictionary.DONE,
16 | },
17 | {
18 | text: 'Cancelado',
19 | value: dictionary.CANCELED,
20 | },
21 | {
22 | text: 'Não iniciado',
23 | value: dictionary.NOT_STARTED,
24 | },
25 | ];
26 |
--------------------------------------------------------------------------------
/resources/js/core/utils/convertKeysToCamelCase.js:
--------------------------------------------------------------------------------
1 | const convertKeysToCamelCase = (data) => {
2 | if (_.isArray(data)) {
3 | return data.map((element) => {
4 | if (_.isObject(element) || _.isArray(element)) {
5 | return convertKeysToCamelCase(element);
6 | }
7 | return element;
8 | });
9 | }
10 | const newData = {};
11 | Object.keys(data).forEach((key) => {
12 | if (_.isObject(data[key]) || _.isArray(data[key])) {
13 | newData[_.camelCase(key)] = convertKeysToCamelCase(data[key]);
14 | } else {
15 | newData[_.camelCase(key)] = data[key];
16 | }
17 | });
18 |
19 | return newData;
20 | };
21 |
22 | export default convertKeysToCamelCase;
23 |
--------------------------------------------------------------------------------
/resources/js/core/utils/convertKeysToSnakeCase.js:
--------------------------------------------------------------------------------
1 | const convertKeysToSnakeCase = (data) => {
2 | if (_.isArray(data)) {
3 | return data.map((element) => {
4 | if (_.isObject(element) || _.isArray(element)) {
5 | return convertKeysToSnakeCase(element);
6 | }
7 | return element;
8 | });
9 | }
10 | const newData = {};
11 | Object.keys(data).forEach((key) => {
12 | if (_.isObject(data[key]) || _.isArray(data[key])) {
13 | newData[_.snakeCase(key)] = convertKeysToSnakeCase(data[key]);
14 | } else {
15 | newData[_.snakeCase(key)] = data[key];
16 | }
17 | });
18 |
19 | return newData;
20 | };
21 |
22 | export default convertKeysToSnakeCase;
23 |
--------------------------------------------------------------------------------
/resources/js/core/utils/generateUUID.js:
--------------------------------------------------------------------------------
1 | const generateUUID = () => { // Public Domain/MIT
2 | var d = new Date().getTime();//Timestamp
3 | var d2 = (performance && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
4 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
5 | var r = Math.random() * 16;//random number between 0 and 16
6 | if(d > 0){//Use timestamp until depleted
7 | r = (d + r)%16 | 0;
8 | d = Math.floor(d/16);
9 | } else {//Use microseconds since page-load if supported
10 | r = (d2 + r)%16 | 0;
11 | d2 = Math.floor(d2/16);
12 | }
13 | return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
14 | });
15 | };
16 |
17 | export default generateUUID;
18 |
--------------------------------------------------------------------------------
/resources/js/core/utils/makeCrudServices.js:
--------------------------------------------------------------------------------
1 | import convertKeysToSnakeCase from './convertKeysToSnakeCase';
2 |
3 | export default function makeCrudServices(entity, apiPath) {
4 | return {
5 | apiPath: `/api/${apiPath}`,
6 | httpClient: axios,
7 |
8 | [`get${_.upperFirst(_.camelCase(entity))}`](payload = {}) {
9 | payload = convertKeysToSnakeCase(payload);
10 |
11 | return axios.get(`/api/${apiPath}`, { params: payload })
12 | .then(response => response.data);
13 | },
14 |
15 | [`read${_.upperFirst(_.camelCase(entity))}`](id) {
16 | return axios.get(`/api/${apiPath}/${id}`)
17 | .then(response => response.data);
18 | },
19 |
20 | [`post${_.upperFirst(_.camelCase(entity))}`](payload) {
21 | payload = convertKeysToSnakeCase(payload);
22 |
23 | return axios.post(`/api/${apiPath}`, payload)
24 | .then(response => response.data);
25 | },
26 |
27 | [`put${_.upperFirst(_.camelCase(entity))}`]({ id, ...payload }) {
28 | payload = convertKeysToSnakeCase(payload);
29 |
30 | return axios.put(`/api/${apiPath}/${id}`, payload)
31 | .then(response => response.data);
32 | },
33 |
34 | [`delete${_.upperFirst(_.camelCase(entity))}`](id) {
35 | return axios.delete(`/api/${apiPath}/${id}`)
36 | .then(response => response.data);
37 | },
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/resources/js/core/utils/makeFormFields.js:
--------------------------------------------------------------------------------
1 | import upperCamelCase from './upperCamelCase';
2 |
3 | export default (module, fields) => {
4 | let computed = {};
5 | fields.forEach((field) => {
6 | computed = {
7 | ...computed,
8 | [field]: {
9 | get() {
10 | return this.$store.getters[`${module}/get${upperCamelCase(field)}`];
11 | },
12 |
13 | set(newValue) {
14 | this.$store.commit(`${module}/set${upperCamelCase(field)}`, newValue, { root: true });
15 | },
16 | },
17 | };
18 | });
19 | return computed;
20 | };
21 |
--------------------------------------------------------------------------------
/resources/js/core/utils/makeMutations.js:
--------------------------------------------------------------------------------
1 | import {
2 | requestState,
3 | successState,
4 | failureState,
5 | } from './requestsStates';
6 |
7 | function callbacksResolver() {
8 | return {
9 | request: (state) => {
10 | Object.assign(state, requestState);
11 | },
12 |
13 | success: (state) => {
14 | Object.assign(state, successState);
15 | },
16 |
17 | failure: (state, errors) => {
18 | Object.assign(state, failureState(errors));
19 | },
20 | };
21 | }
22 |
23 | export default function makeMutations(requestName) {
24 | const { request, success, failure } = callbacksResolver();
25 |
26 | return {
27 | [`${requestName}Request`]: request,
28 | [`${requestName}Success`]: success,
29 | [`${requestName}Failure`]: failure,
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/resources/js/core/utils/makeRequestStore.js:
--------------------------------------------------------------------------------
1 | import { initialState } from './requestsStates';
2 | import makeMutations from './makeMutations';
3 |
4 | export default (module, namespaced = false) => {
5 | const name = Object.keys(module)[0];
6 |
7 | return {
8 | [name]: {
9 | namespaced,
10 | state: { ...initialState },
11 |
12 | actions: {
13 | [name]({ commit }, params) {
14 | commit(`${name}Request`);
15 |
16 | return module[name](params)
17 | .then(({ data }) => {
18 | commit(`${name}Success`);
19 | return data;
20 | })
21 | .catch(error => commit(`${name}Failure`, error));
22 | },
23 | },
24 |
25 | mutations: {
26 | ...makeMutations(name),
27 | },
28 | }
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/resources/js/core/utils/requestsStates/failureState.js:
--------------------------------------------------------------------------------
1 | import initialState from './initialState';
2 |
3 | const failureState = errors => ({
4 | ...initialState,
5 | isFetching: false,
6 | hasFailed: true,
7 | errors,
8 | });
9 |
10 | export default failureState;
11 |
--------------------------------------------------------------------------------
/resources/js/core/utils/requestsStates/index.js:
--------------------------------------------------------------------------------
1 | export { default as initialState } from './initialState';
2 | export { default as requestState } from './requestState';
3 | export { default as successState } from './successState';
4 | export { default as failureState } from './failureState';
5 |
--------------------------------------------------------------------------------
/resources/js/core/utils/requestsStates/initialState.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | isFetching: false,
3 | hasFailed: false,
4 | hasSucceeded: false,
5 | errors: {},
6 | };
7 |
8 | export default initialState;
9 |
--------------------------------------------------------------------------------
/resources/js/core/utils/requestsStates/requestState.js:
--------------------------------------------------------------------------------
1 | import initialState from './initialState';
2 |
3 | const requestState = {
4 | ...initialState,
5 | isFetching: true,
6 | };
7 |
8 | export default requestState;
9 |
--------------------------------------------------------------------------------
/resources/js/core/utils/requestsStates/successState.js:
--------------------------------------------------------------------------------
1 | import initialState from './initialState';
2 |
3 | const successState = {
4 | ...initialState,
5 | hasSucceeded: true,
6 | };
7 |
8 | export default successState;
9 |
--------------------------------------------------------------------------------
/resources/js/core/utils/upperCamelCase.js:
--------------------------------------------------------------------------------
1 | import lodash from 'lodash';
2 |
3 | const upperCamelCase = string => lodash.upperFirst(lodash.camelCase(string));
4 |
5 | export default upperCamelCase;
6 |
--------------------------------------------------------------------------------
/resources/js/http.js:
--------------------------------------------------------------------------------
1 | import AxiosCache from 'axios-cache-plugin';
2 |
3 | const axios = require('axios');
4 |
5 | let token = document.head.querySelector('meta[name="csrf-token"]');
6 |
7 | if (!token) {
8 | console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
9 | }
10 |
11 | let typeUser = document.head.querySelector('meta[name="type-user"]');
12 |
13 | let axiosInstance = axios.create({
14 | withCredentials: false,
15 | headers: {
16 | 'X-Requested-With': 'XMLHttpRequest',
17 | 'X-CSRF-TOKEN': (token.content || null),
18 | },
19 | });
20 |
21 | const wrapper = AxiosCache(axiosInstance, {
22 | maxCacheSize: 15 * 60 * 1000,
23 | });
24 | export default wrapper;
25 |
--------------------------------------------------------------------------------
/resources/js/modules/board/app.js:
--------------------------------------------------------------------------------
1 | require('../../bootstrap');
2 |
3 | import Vue from 'vue'
4 | import VueRouter from 'vue-router';
5 | import vuetify from '../../vuetify';
6 | import App from './views/App.vue';
7 | import draggable from 'vuedraggable';
8 | import {
9 | ValidationProvider,
10 | ValidationObserver,
11 | extend,
12 | setInteractionMode,
13 | } from 'vee-validate';
14 | import moment from 'moment';
15 |
16 | import validateLocale from 'vee-validate/dist/locale/pt_BR.json';
17 | import routes from './routes';
18 | import store from './store';
19 |
20 | setInteractionMode('lazy'); //Mode interation validation fields;
21 |
22 | //Import rules
23 | import * as rules from 'vee-validate/dist/rules';
24 |
25 | for (let rule in rules) {
26 | extend(rule, {
27 | ...rules[rule], // add the rule
28 | message: validateLocale.messages[rule] // add its message
29 | });
30 | }
31 |
32 | Vue.use(VueRouter);
33 |
34 | // Routes
35 | const router = new VueRouter({
36 | mode: 'history',
37 | routes,
38 | });
39 |
40 | // emite evento customizado para capturar mudanças de rota sem depender do Vue
41 | router.afterEach((to, from) => {
42 | const event = new CustomEvent('routeChange', {
43 | detail: {
44 | to,
45 | from,
46 | },
47 | });
48 |
49 | window.dispatchEvent(event);
50 | });
51 |
52 | Vue.component('ValidationObserver', ValidationObserver);
53 | Vue.component('ValidationProvider', ValidationProvider);
54 | Vue.component('Draggable', draggable);
55 |
56 | Vue.prototype.$isChristmasSeason = (function isChristmasSeason() {
57 | const today = moment();
58 | const currentYear = today.year();
59 | const christmasSeasonStartDate = moment(`${currentYear}-12-10`);
60 | const christmasSeasonEndDate = moment(`${+currentYear + 1}-01-02`);
61 |
62 | return today.isBetween(christmasSeasonStartDate, christmasSeasonEndDate);
63 | }());
64 |
65 | const app = new Vue({
66 | render: (h) => h(App),
67 | vuetify,
68 | router,
69 | store,
70 | }).$mount('#app');
71 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/AcceptanceCriteriaForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 | {{ criteria }}
13 |
14 |
15 |
16 |
22 | Editar
23 |
24 |
25 |
26 |
32 | Excluir
33 |
34 |
35 |
36 |
37 |
38 |
39 |
48 |
49 |
52 | {{ addButtonText }}
53 |
54 |
55 |
56 |
57 |
58 |
97 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/ArtifactItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ artifact.description }}
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
30 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/BoardContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
45 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/InlineDeleteConfirmation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | Excluir
11 |
12 |
15 |
16 | {{ confirmMessage }}
17 |
18 |
19 | Essa ação não poderá ser desfeita
20 |
21 |
22 |
23 |
29 | Sim
30 |
31 |
37 | Não
38 |
39 |
40 |
41 |
42 |
57 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/LabelItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 | {{ label.name }}
14 |
15 |
16 |
17 |
35 |
43 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/LabelList.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
78 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/LabelSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
51 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/LinkChip.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
16 | {{ computedLink.label }}
17 |
18 |
19 |
25 | open_in_new
26 |
27 |
28 |
94 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/ListContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/ListSkeletonLoader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/MemberList.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
76 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/SwitchButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 | switch {{ value }}
15 |
16 |
17 |
18 |
51 |
59 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/TeamChip.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 | {{ getName() }}
12 |
13 |
14 |
15 |
44 |
65 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/TooltipRating.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
8 |
11 |
12 |
21 | {{ icon }}
22 |
23 |
24 | {{ tooltipMessage(props.index).tooltip }}
25 |
26 |
27 |
28 |
29 |
30 | Selecionado: {{ tooltipMessage(internalValue - 1).value }}
31 |
32 |
33 |
34 |
35 |
83 |
91 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/UserStoryPipeline.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
10 |
16 |
19 |
25 |
28 |
34 |
35 |
36 |
37 |
88 |
--------------------------------------------------------------------------------
/resources/js/modules/board/components/UserStoryPipelineItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 | {{ status }}
15 |
16 |
17 |
35 |
50 |
--------------------------------------------------------------------------------
/resources/js/modules/board/constants/BoardKeys.js:
--------------------------------------------------------------------------------
1 | export const NOT_PLANNED = 'notPlanned';
2 | export const IMPEDIMENTS = 'impediments';
3 | export const SPRINT_DEVLOG = 'sprintDevlog';
4 | export const SPRINT_BACKLOG = 'sprintBacklog';
5 | export const KAIZEN = 'kaizen';
6 |
--------------------------------------------------------------------------------
/resources/js/modules/board/constants/BoardListKeys.js:
--------------------------------------------------------------------------------
1 | export const TODO = 'todo';
2 | export const DEVELOPMENT = 'development';
3 | export const CODE_REVIEW = 'codeReview';
4 | export const DONE = 'done';
5 | export const DEPLOY = 'deploy';
6 | export const BUGS = 'bugs';
7 | export const DEVLOG = 'devlog';
8 | export const BACKLOG = 'backlog';
9 | export const HELPDESK = 'helpDesk';
10 | export const DEVTASK = 'devtask';
11 | export const AVENGERS = 'avangers';
12 | export const STEPPER = 'stepper';
13 | export const BREAKOUT_ONE = 'breakoutOne';
14 | export const SYS_IN = 'sysIn';
15 | export const SYS_OUT = 'sysOut';
16 | export const GRASSHOPPER = 'grasshopper';
17 | export const NOT_PRIORITIZED = 'notPrioritized';
18 |
19 | export default {
20 | TODO,
21 | DEVELOPMENT,
22 | CODE_REVIEW,
23 | DONE,
24 | DEPLOY,
25 | BUGS,
26 | DEVLOG,
27 | BACKLOG,
28 | HELPDESK,
29 | DEVTASK,
30 | AVENGERS,
31 | STEPPER,
32 | BREAKOUT_ONE,
33 | SYS_IN,
34 | SYS_OUT,
35 | NOT_PRIORITIZED,
36 | };
37 |
--------------------------------------------------------------------------------
/resources/js/modules/board/constants/CardTypes.js:
--------------------------------------------------------------------------------
1 | export const USER_STORY = 'user-story';
2 | export const NOT_PRIORITIZED = 'not-prioritized';
3 | export const TASK = 'task';
4 |
5 | export default {
6 | USER_STORY,
7 | NOT_PRIORITIZED,
8 | TASK,
9 | };
10 |
--------------------------------------------------------------------------------
/resources/js/modules/board/constants/LabelKeys.js:
--------------------------------------------------------------------------------
1 | export const BACKEND = 'backend';
2 | export const FRONTEND = 'frontend';
3 | export const MOCKUP = 'mockup';
4 | export const SECURITY = 'secutiry';
5 | export const BUG = 'bug';
6 | export const DOCUMENTATION = 'documentation';
7 | export const INFRA = 'infra';
8 | export const HELP_DESK = 'helpDesk';
9 | export const MEET = 'meet';
10 | export const UX = 'ux';
11 | export const APP = 'app';
12 | export const EXPORT = 'export';
13 |
14 | export default {
15 | BACKEND,
16 | FRONTEND,
17 | MOCKUP,
18 | SECURITY,
19 | BUG,
20 | DOCUMENTATION,
21 | INFRA,
22 | HELP_DESK,
23 | MEET,
24 | UX,
25 | APP,
26 | EXPORT,
27 | };
28 |
--------------------------------------------------------------------------------
/resources/js/modules/board/constants/PlanningGroups.js:
--------------------------------------------------------------------------------
1 | const PLANNING = 'planning';
2 | const PROBLEMS = 'problems';
3 |
4 | export default {
5 | PLANNING,
6 | PROBLEMS
7 | };
8 |
--------------------------------------------------------------------------------
/resources/js/modules/board/routes/index.js:
--------------------------------------------------------------------------------
1 | import settingsRoutes from '../../settings/routes';
2 | import processesRoutes from '../../processes/routes';
3 | import reportsRoutes from '../../reports/routes';
4 | import milestonesRoutes from '../../milestones/routes';
5 |
6 | import Home from '../views/Home.vue';
7 | import Planning from '../views/Planning.vue';
8 | import Sprint from '../views/Sprint.vue';
9 | import WorkspaceSelection from '../views/WorkspaceSelection.vue';
10 | import CompanyPlanning from '../views/CompanyPlanning.vue';
11 |
12 | export default [
13 | {
14 | path: '/home',
15 | name: 'home',
16 | component: Home,
17 | meta: {
18 | title: 'Home',
19 | },
20 | },
21 | {
22 | path: '/workspace/:workspaceId/planning',
23 | name: 'planning',
24 | component: Planning,
25 | meta: {
26 | title: 'Planning',
27 | },
28 | },
29 | {
30 | path: '/workspace/:workspaceId/sprint/:teamId',
31 | name: 'sprint',
32 | component: Sprint,
33 | meta: {
34 | title: 'Sprint',
35 | },
36 | props: true,
37 | },
38 | {
39 | path: '/workspace/select',
40 | name: 'workspace.select',
41 | component: WorkspaceSelection,
42 | meta: {
43 | title: 'Selecione seu Workspace',
44 | },
45 | },
46 | {
47 | path: '/workspace/company',
48 | name: 'workspace.company',
49 | component: CompanyPlanning,
50 | meta: {
51 | title: 'Backlogs',
52 | },
53 | },
54 | ...settingsRoutes,
55 | ...processesRoutes,
56 | ...reportsRoutes,
57 | ...milestonesRoutes,
58 | ];
59 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/boards.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getBoards = () => http.get('/boards');
4 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/cards.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const createCard = (payload) => http.post('/cards', payload);
4 |
5 | export const createCards = (payload) => http.post('/cards/store-many', payload);
6 |
7 | export const getTaskCardsFromUserStory = (params) => http.get('/cards/from-user-story', {
8 | params,
9 | });
10 |
11 | export const getTaskCardsFromDevlog = (params) => http.get('/cards/from-devlog', {
12 | params,
13 | });
14 |
15 | export const getTaskCardsFromNotPlanned = (params) => http.get('/cards/from-not-planned', {
16 | params,
17 | });
18 |
19 | export const getTaskCardsFromKaizen = (params) => http.get('/cards/from-kaizen', {
20 | params,
21 | });
22 |
23 | export const getCompanyPlanningCards = (params) => http.get('/cards/from-company-planning', {
24 | params,
25 | });
26 |
27 | export const getPlanningCards = (params) => http.get('/cards/from-planning', {
28 | params,
29 | });
30 |
31 | export const updateCard = ({ id, ...params }) => http.put(`/cards/${id}`, params);
32 |
33 | export const deleteCard = (id) => http.delete(`/cards/${id}`);
34 |
35 | export const deleteManyCards = (payload) => http.delete('/cards/delete-many', { data: payload });
36 |
37 | export const updateCardsPositions = (cards = []) => http.post('/cards/update-positions', { cards });
38 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/events.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const createEvent = (payload) => http.post('/events', payload);
4 |
5 | export const updateEvent = ({ id, ...params }) => http.put(`/events/${id}`, params);
6 |
7 | export const deleteEvent = (id) => http.delete(`/events/${id}`);
8 |
9 | export const getEventsByTeam = (teamId) => http.get(`/events/${teamId}`);
10 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/goals.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const createGoal = (payload) => http.post('/goals', payload);
4 |
5 | export const updateGoal = ({ id, ...params }) => http.put(`/goals/${id}`, params);
6 |
7 | export const deleteGoal = (id) => http.delete(`/goals/${id}`);
8 |
9 | export const getGoals = () => http.get(`/goals/`);
10 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/planning.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getPlanningLists = (workspaceId) => http.get(`/lists/planning/${workspaceId}`);
4 |
5 | export const getProblemsLists = () => http.get('/lists/problems');
6 |
7 | export const getCompanyPlanningLists = () => http.get('/lists/planning/company');
8 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/sprint.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getDefaultLists = (params) => http.get('/lists/default', { params });
4 |
5 | export const getDevlogLists = (params) => http.get('/lists/devlog', { params });
6 |
7 | export const getPlanningLists = () => http.get('/lists/planning');
8 |
9 | export const getCurrentSprintSummaryByTeam = (teamId) => http.get(`/sprint/summary/current/${teamId}`);
10 |
11 | export const getCurrentSprintOverviewByTeam = (teamId) => http.get(`/reports/sprint-overview/${teamId}`);
12 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/sprintReport.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const createSprintReport = (payload) => http.post('/sprint-reports', payload);
4 |
5 | export const getSprintReports = () => http.get('/sprint-reports');
6 |
--------------------------------------------------------------------------------
/resources/js/modules/board/services/userStories.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getUserStoriesByTeam = (teamId) => http.get(`/user-stories/${teamId}`);
4 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/boards.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getBoards,
6 | } from '../services/boards';
7 |
8 | const modules = [
9 | { getBoards },
10 | ];
11 |
12 | export default {
13 | namespaced: true,
14 | modules: {
15 | ...modules.reduce((acc, module) => ({
16 | ...acc,
17 | ...makeRequestStore(module),
18 | }), {}),
19 | },
20 | state: {
21 | items: [],
22 | },
23 | mutations: {
24 | setItems(state, payload) {
25 | state.items = convertKeysToCamelCase(payload);
26 | },
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/cards.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 |
3 | import {
4 | deleteCard,
5 | deleteManyCards,
6 | createCard,
7 | updateCard,
8 | updateCardsPositions,
9 | createCards,
10 | } from '../services/cards';
11 |
12 | const modules = [
13 | { deleteCard },
14 | { deleteManyCards },
15 | { createCard },
16 | { updateCard },
17 | { updateCardsPositions },
18 | { createCards },
19 | ];
20 |
21 | export default {
22 | namespaced: true,
23 | modules: {
24 | ...modules.reduce((acc, module) => ({
25 | ...acc,
26 | ...makeRequestStore(module),
27 | }), {}),
28 | },
29 | }
30 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/events.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 |
3 | import {
4 | deleteEvent,
5 | createEvent,
6 | updateEvent,
7 | } from '../services/events';
8 |
9 | const modules = [
10 | { deleteEvent },
11 | { createEvent },
12 | { updateEvent },
13 | ];
14 |
15 | export default {
16 | namespaced: true,
17 | modules: {
18 | ...modules.reduce((acc, module) => ({
19 | ...acc,
20 | ...makeRequestStore(module),
21 | }), {}),
22 | },
23 | }
24 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/goals.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 | import { BACKLOG } from '../constants/BoardListKeys';
4 |
5 | import {
6 | deleteGoal,
7 | createGoal,
8 | updateGoal,
9 | getGoals,
10 | } from '../services/goals';
11 |
12 | const modules = [
13 | { deleteGoal },
14 | { createGoal },
15 | { updateGoal },
16 | { getGoals },
17 | ];
18 |
19 | export default {
20 | namespaced: true,
21 | modules: {
22 | ...modules.reduce((acc, module) => ({
23 | ...acc,
24 | ...makeRequestStore(module),
25 | }), {}),
26 | },
27 | state: {
28 | items: [],
29 | },
30 | getters: {
31 | getGoalByKey(state, _, rootState, rootGetters) {
32 | return (key) => {
33 | if(key.split('-').includes(BACKLOG)) {
34 | return state.items.filter(({ workspaceId }) => {
35 | return workspaceId === rootGetters['workspaces/currentWorkspace']?.id || (key.split('-')[1] || null) === workspaceId;
36 | })[0];
37 | }
38 |
39 | const team = rootState['teams'].items.filter((team) => team.key === key)[0];
40 | return state.items.filter(({ teamId }) => {
41 | return teamId === team.id;
42 | })[0];
43 | }
44 | },
45 |
46 | getGoalByTeamId(state) {
47 | return (id) => {
48 | return state.items.filter(({ teamId }) => {
49 | return teamId === id;
50 | })[0];
51 | }
52 | },
53 | },
54 | mutations: {
55 | setItems(state, payload) {
56 | state.items = convertKeysToCamelCase(payload);
57 | },
58 | },
59 | }
60 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 |
4 | import cards from './cards';
5 | import boards from './boards';
6 | import sprint from './sprint';
7 | import events from './events';
8 | import goals from './goals';
9 | import planning from './planning';
10 | import sprintReport from './sprintReport';
11 |
12 | import settingsModules from '../../settings/store';
13 | import processesModules from '../../processes/store';
14 | import reportsModules from '../../reports/store';
15 | import milestonesModules from '../../milestones/store';
16 |
17 | Vue.use(Vuex);
18 |
19 | export default new Vuex.Store({
20 | namespaced: true,
21 | modules: {
22 | cards,
23 | boards,
24 | sprint,
25 | events,
26 | goals,
27 | planning,
28 | sprintReport,
29 | ...settingsModules,
30 | ...processesModules,
31 | ...reportsModules,
32 | ...milestonesModules,
33 | },
34 | });
35 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/planning.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 |
3 | import {
4 | getPlanningLists,
5 | getCompanyPlanningLists,
6 | } from '../services/planning';
7 |
8 | const modules = [
9 | { getPlanningLists },
10 | { getCompanyPlanningLists },
11 | ];
12 |
13 | export default {
14 | namespaced: true,
15 | modules: {
16 | ...modules.reduce((acc, module) => ({
17 | ...acc,
18 | ...makeRequestStore(module),
19 | }), {}),
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/sprint.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 |
3 | import {
4 | getCurrentSprintSummaryByTeam,
5 | getCurrentSprintOverviewByTeam,
6 | getDefaultLists,
7 | getDevlogLists,
8 | } from '../services/sprint';
9 |
10 | const modules = [
11 | { getCurrentSprintSummaryByTeam },
12 | { getCurrentSprintOverviewByTeam },
13 | { getDefaultLists },
14 | { getDevlogLists },
15 | { getDefaultLists },
16 | ];
17 |
18 | export default {
19 | namespaced: true,
20 | modules: {
21 | ...modules.reduce((acc, module) => ({
22 | ...acc,
23 | ...makeRequestStore(module),
24 | }), {}),
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/resources/js/modules/board/store/sprintReport.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 |
3 | import {
4 | createSprintReport,
5 | getSprintReports,
6 | } from '../services/sprintReport';
7 |
8 | const modules = [
9 | { createSprintReport },
10 | { getSprintReports },
11 | ];
12 |
13 | export default {
14 | namespaced: true,
15 | modules: {
16 | ...modules.reduce((acc, module) => ({
17 | ...acc,
18 | ...makeRequestStore(module),
19 | }), {}),
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/resources/js/modules/board/views/CompanyPlanning.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
16 |
17 |
18 |
19 | list
20 |
21 |
Planejamento de Backlogs
22 |
23 |
24 |
25 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
80 |
--------------------------------------------------------------------------------
/resources/js/modules/board/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | {{ message }}
10 |
11 |
14 |
18 | Tentar novamente
19 |
20 |
21 |
22 |
23 |
24 |
25 |
75 |
--------------------------------------------------------------------------------
/resources/js/modules/board/views/Sprint.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
17 | {{ team.name }}
18 |
19 |
24 |
25 |
32 | {{ getGoalByTeamId(team.id).title }}
33 |
34 |
35 |
38 |
39 |
40 |
41 |
42 |
43 |
67 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/components/MilestoneSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
20 |
21 |
22 |
61 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/components/TeamsSelect.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
23 |
24 |
25 |
64 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/routes/index.js:
--------------------------------------------------------------------------------
1 | import Home from '../views/Home.vue';
2 | import Board from '../views/Board.vue';
3 |
4 | export default [
5 | {
6 | path: '/milestone',
7 | name: 'milestones',
8 | component: Home,
9 | meta: {
10 | title: 'Milestones',
11 | },
12 | },
13 | {
14 | path: '/milestones/:id/board',
15 | name: 'milestones.board',
16 | component: Board,
17 | meta: {
18 | title: 'Milestones',
19 | },
20 | props: true,
21 | },
22 | ];
23 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/services/milestones.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getMilestones = () => http.get('/milestones');
4 |
5 | export const getMilestone = (id) => http.get(`/milestones/${id}`);
6 |
7 | export const createMilestone = (payload) => http.post('/milestones', payload);
8 |
9 | export const updateMilestone = ({ id, ...params }) => http.put(`/milestones/${id}`, params);
10 |
11 | export const deleteMilestone = (id) => http.delete(`/milestones/${id}`);
12 |
13 | export const getNotStartedItems = (id) => http.get(`/milestones/${id}/backlog-items/not-started`);
14 |
15 | export const getOnGoingItems = (id) => http.get(`/milestones/${id}/backlog-items/on-going`);
16 |
17 | export const getFinishedItems = (id) => http.get(`/milestones/${id}/backlog-items/finished`);
18 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/store/index.js:
--------------------------------------------------------------------------------
1 | import milestones from './milestones';
2 |
3 | export default {
4 | milestones,
5 | };
6 |
--------------------------------------------------------------------------------
/resources/js/modules/milestones/store/milestones.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getMilestones,
6 | getMilestone,
7 | createMilestone,
8 | updateMilestone,
9 | deleteMilestone,
10 | getFinishedItems,
11 | getOnGoingItems,
12 | getNotStartedItems,
13 | } from '../services/milestones';
14 |
15 | const modules = [
16 | { getMilestones },
17 | { getMilestone },
18 | { createMilestone },
19 | { updateMilestone },
20 | { deleteMilestone },
21 | { getFinishedItems },
22 | { getOnGoingItems },
23 | { getNotStartedItems },
24 | ];
25 |
26 | export default {
27 | namespaced: true,
28 | modules: {
29 | ...modules.reduce((acc, module) => ({
30 | ...acc,
31 | ...makeRequestStore(module),
32 | }), {}),
33 | },
34 | state: {
35 | items: [],
36 | selectedWorkspaceId: null,
37 | },
38 | getters: {
39 | itemsByWorkspace(state, _, __, rootGetters) {
40 | return state.items.filter(({ workspaceId }) => {
41 | return workspaceId === rootGetters['workspaces/currentWorkspace']?.id;
42 | });
43 | },
44 | },
45 | mutations: {
46 | setItems(state, payload) {
47 | state.items = convertKeysToCamelCase(payload);
48 | },
49 | setSelectedWorkspaceId(state, payload) {
50 | state.selectedWorkspaceId = payload;
51 | },
52 | },
53 | };
54 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/components/ViewProcessModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | {{ process.name }}
9 |
10 |
11 |
12 |
13 |
16 |
20 |
21 | {{ item.title }}
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
66 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/routes/index.js:
--------------------------------------------------------------------------------
1 | import Home from '../views/Home.vue';
2 | import Create from '../views/Create.vue';
3 | import Edit from '../views/Edit.vue';
4 |
5 | export default [
6 | {
7 | path: '/process',
8 | name: 'processes',
9 | component: Home,
10 | meta: {
11 | title: 'Central de Processos',
12 | },
13 | },
14 | {
15 | path: '/process/edit/:id',
16 | name: 'processes.edit',
17 | component: Edit,
18 | meta: {
19 | title: 'Editar Processo',
20 | },
21 | props: true,
22 | },
23 | {
24 | path: '/process/new',
25 | name: 'processes.new',
26 | component: Create,
27 | meta: {
28 | title: 'Novo Processo',
29 | },
30 | },
31 | ];
32 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/services/processes.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getProcesses = () => http.get('/processes');
4 |
5 | export const getProcess = (id) => http.get(`/processes/${id}`);
6 |
7 | export const createProcess = (payload) => http.post('/processes', payload);
8 |
9 | export const updateProcess = ({ id, ...params }) => http.put(`/processes/${id}`, params);
10 |
11 | export const deleteProcess = (id) => http.delete(`/processes/${id}`);
12 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/store/index.js:
--------------------------------------------------------------------------------
1 | import processes from './processes';
2 |
3 | export default {
4 | processes,
5 | };
6 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/store/processes.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getProcess,
6 | getProcesses,
7 | createProcess,
8 | updateProcess,
9 | deleteProcess,
10 | } from '../services/processes';
11 |
12 | const modules = [
13 | { getProcess },
14 | { getProcesses },
15 | { createProcess },
16 | { updateProcess },
17 | { deleteProcess },
18 | ];
19 |
20 | export default {
21 | namespaced: true,
22 | modules: {
23 | ...modules.reduce((acc, module) => ({
24 | ...acc,
25 | ...makeRequestStore(module),
26 | }), {}),
27 | },
28 | state: {
29 | items: [],
30 | },
31 | getters: {
32 | itemsByTeam(state) {
33 | return (teamId) => state.items.filter(({ teamIds }) => {
34 | return teamIds.indexOf(teamId) > -1;
35 | })
36 | },
37 | },
38 | mutations: {
39 | setItems(state, payload) {
40 | state.items = convertKeysToCamelCase(payload);
41 | },
42 | },
43 | }
44 |
--------------------------------------------------------------------------------
/resources/js/modules/processes/views/Create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
56 |
--------------------------------------------------------------------------------
/resources/js/modules/reports/routes/index.js:
--------------------------------------------------------------------------------
1 | import Home from '../views/Home.vue';
2 | import SprintReport from '../views/SprintReport.vue';
3 |
4 | export default [
5 | {
6 | path: '/reports',
7 | name: 'reports',
8 | component: Home,
9 | meta: {
10 | title: 'Relatórios',
11 | },
12 | },
13 | {
14 | path: '/reports/sprints',
15 | name: 'reports.sprint',
16 | component: SprintReport,
17 | meta: {
18 | title: 'Relatórios de Sprint',
19 | },
20 | },
21 | ];
22 |
--------------------------------------------------------------------------------
/resources/js/modules/reports/services/sprintReports.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | // eslint-disable-next-line import/prefer-default-export
4 | export const getSprintReportByTeamId = (teamId) => http.get(`/sprint-reports/${teamId}`);
5 |
--------------------------------------------------------------------------------
/resources/js/modules/reports/store/index.js:
--------------------------------------------------------------------------------
1 | import sprintReports from './sprintReports';
2 |
3 | export default {
4 | sprintReports,
5 | };
6 |
--------------------------------------------------------------------------------
/resources/js/modules/reports/store/sprintReports.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getSprintReportByTeamId,
6 | } from '../services/sprintReports';
7 |
8 | const modules = [
9 | { getSprintReportByTeamId },
10 | ];
11 |
12 | export default {
13 | namespaced: true,
14 | modules: {
15 | ...modules.reduce((acc, module) => ({
16 | ...acc,
17 | ...makeRequestStore(module),
18 | }), {}),
19 | },
20 | state: {
21 | items: [],
22 | selectedTeamId: null,
23 | },
24 | getters: {
25 | itemsByTeam(state) {
26 | return (teamId) => state.items.filter(({ teamIds }) => {
27 | return teamIds.indexOf(teamId) > -1;
28 | });
29 | },
30 | },
31 | mutations: {
32 | setItems(state, payload) {
33 | state.items = convertKeysToCamelCase(payload);
34 | },
35 |
36 | setSelectedTeamId(state, payload) {
37 | state.selectedTeamId = payload;
38 | },
39 | },
40 | }
41 |
--------------------------------------------------------------------------------
/resources/js/modules/reports/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | Sprints
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/routes/index.js:
--------------------------------------------------------------------------------
1 | import Home from '../views/Home.vue';
2 | import Members from '../views/Members.vue';
3 | import Workspaces from '../views/Workspaces.vue';
4 | import Teams from '../views/Teams.vue';
5 | import Labels from '../views/Labels.vue';
6 | import BacklogLabels from '../views/BacklogLabels.vue';
7 |
8 | export default [
9 | {
10 | path: '/settings/workspaces',
11 | name: 'settings.workspaces',
12 | component: Workspaces,
13 | meta: {
14 | title: 'Workspaces',
15 | },
16 | },
17 | {
18 | path: '/settings/members',
19 | name: 'settings.members',
20 | component: Members,
21 | meta: {
22 | title: 'Membros',
23 | },
24 | },
25 | {
26 | path: '/settings/teams',
27 | name: 'settings.teams',
28 | component: Teams,
29 | meta: {
30 | title: 'Times',
31 | },
32 | },
33 | {
34 | path: '/settings/labels',
35 | name: 'settings.labels',
36 | component: Labels,
37 | meta: {
38 | title: 'Categorias',
39 | },
40 | },
41 | {
42 | path: '/settings/backlog-labels',
43 | name: 'settings.backlogLabels',
44 | component: BacklogLabels,
45 | meta: {
46 | title: 'Categorias de backlog',
47 | },
48 | },
49 | {
50 | path: '/settings',
51 | name: 'settings',
52 | component: Home,
53 | meta: {
54 | title: 'Configurações',
55 | },
56 | },
57 | ];
58 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/services/backlogLabels.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getBacklogLabels = () => http.get('/backlog-labels');
4 |
5 | export const createBacklogLabel = (payload) => http.post('/backlog-labels', payload);
6 |
7 | export const updateBacklogLabel = ({ id, ...params }) => http.put(`/backlog-labels/${id}`, params);
8 |
9 | export const deleteBacklogLabel = (id) => http.delete(`/backlog-labels/${id}`);
10 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/services/labels.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getLabels = () => http.get('/labels');
4 |
5 | export const createLabel = (payload) => http.post('/labels', payload);
6 |
7 | export const updateLabel = ({ id, ...params }) => http.put(`/labels/${id}`, params);
8 |
9 | export const deleteLabel = (id) => http.delete(`/labels/${id}`);
10 |
11 | export const getLabelsByWorkspaceId = (workspaceId) => http.get(`/labels/workspace/${workspaceId}`);
12 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/services/members.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getMembers = () => http.get('/members');
4 |
5 | export const createMember = (payload) => http.post('/members', payload);
6 |
7 | export const updateMember = ({ id, ...params }) => http.put(`/members/${id}`, params);
8 |
9 | export const deleteMember = (id) => http.delete(`/members/${id}`);
10 |
11 | export const resendWelcomeMail = (userId) => http.post('/users/resend-welcome-mail', { user_id: userId });
12 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/services/teams.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getTeams = () => http.get('/teams');
4 |
5 | export const createTeam = (payload) => http.post('/teams', payload);
6 |
7 | export const updateTeam = ({ id, ...params }) => http.put(`/teams/${id}`, params);
8 |
9 | export const deleteTeam = (id) => http.delete(`/teams/${id}`);
10 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/services/workspaces.js:
--------------------------------------------------------------------------------
1 | import http from '../../../http';
2 |
3 | export const getWorkspaces = () => http.get('/workspaces');
4 |
5 | export const getWorkspacesIncludeInactive = () => http.get('/workspaces', { params: { with_inactive: true } });
6 |
7 | export const createWorkspace = (payload) => http.post('/workspaces', payload);
8 |
9 | export const updateWorkspace = ({ id, ...params }) => http.put(`/workspaces/${id}`, params);
10 |
11 | export const deleteWorkspace = (id) => http.delete(`/workspaces/${id}`);
12 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/backlogLabels.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getBacklogLabels,
6 | createBacklogLabel,
7 | updateBacklogLabel,
8 | deleteBacklogLabel,
9 | } from '../services/backlogLabels';
10 |
11 | const modules = [
12 | { getBacklogLabels },
13 | { createBacklogLabel },
14 | { updateBacklogLabel },
15 | { deleteBacklogLabel },
16 | ];
17 |
18 | export default {
19 | namespaced: true,
20 | modules: {
21 | ...modules.reduce((acc, module) => ({
22 | ...acc,
23 | ...makeRequestStore(module),
24 | }), {}),
25 | },
26 | state: {
27 | items: [],
28 | },
29 | mutations: {
30 | setItems(state, payload) {
31 | state.items = convertKeysToCamelCase(payload);
32 | },
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/index.js:
--------------------------------------------------------------------------------
1 | import workspaces from './workspaces';
2 | import members from './members';
3 | import teams from './teams';
4 | import labels from './labels';
5 | import backlogLabels from './backlogLabels';
6 |
7 | export default {
8 | workspaces,
9 | members,
10 | teams,
11 | labels,
12 | backlogLabels,
13 | };
14 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/labels.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getLabels,
6 | createLabel,
7 | updateLabel,
8 | deleteLabel,
9 | getLabelsByWorkspaceId,
10 | } from '../services/labels';
11 |
12 | const modules = [
13 | { getLabels },
14 | { createLabel },
15 | { updateLabel },
16 | { deleteLabel },
17 | { getLabelsByWorkspaceId },
18 | ];
19 |
20 | export default {
21 | namespaced: true,
22 | modules: {
23 | ...modules.reduce((acc, module) => ({
24 | ...acc,
25 | ...makeRequestStore(module),
26 | }), {}),
27 | },
28 | state: {
29 | items: [],
30 | selectedWorkspaceId: null,
31 | },
32 | getters: {
33 | itemsByWorkspace(state, _, __, rootGetters) {
34 | return state.items.filter(({ workspaceId }) => {
35 | return workspaceId === rootGetters['workspaces/currentWorkspace']?.id;
36 | });
37 | },
38 | },
39 | mutations: {
40 | setItems(state, payload) {
41 | state.items = convertKeysToCamelCase(payload);
42 | },
43 | setSelectedWorkspaceId(state, payload) {
44 | state.selectedWorkspaceId = payload;
45 | },
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/members.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getMembers,
6 | createMember,
7 | updateMember,
8 | deleteMember,
9 | resendWelcomeMail,
10 | } from '../services/members';
11 |
12 | const modules = [
13 | { getMembers },
14 | { createMember },
15 | { updateMember },
16 | { deleteMember },
17 | { resendWelcomeMail },
18 | ];
19 |
20 | export default {
21 | namespaced: true,
22 | modules: {
23 | ...modules.reduce((acc, module) => ({
24 | ...acc,
25 | ...makeRequestStore(module),
26 | }), {}),
27 | },
28 | state: {
29 | items: [],
30 | },
31 | getters: {
32 | itemsByWorkspace(state, _, __, rootGetters) {
33 | return state.items.filter(({ workspaceIds }) => {
34 | return workspaceIds.indexOf(rootGetters['workspaces/currentWorkspace']?.id) > -1;
35 | });
36 | },
37 | },
38 | mutations: {
39 | setItems(state, payload) {
40 | state.items = convertKeysToCamelCase(payload);
41 | },
42 | },
43 | };
44 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/teams.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getTeams,
6 | createTeam,
7 | updateTeam,
8 | deleteTeam,
9 | } from '../services/teams';
10 |
11 | const modules = [
12 | { getTeams },
13 | { createTeam },
14 | { updateTeam },
15 | { deleteTeam },
16 | ];
17 |
18 | export default {
19 | namespaced: true,
20 | modules: {
21 | ...modules.reduce((acc, module) => ({
22 | ...acc,
23 | ...makeRequestStore(module),
24 | }), {}),
25 | },
26 | state: {
27 | items: [],
28 | },
29 | getters: {
30 | itemsByWorkspace(state, _, __, rootGetters) {
31 | return state.items.filter(({ workspaceId }) => {
32 | return workspaceId?.indexOf(rootGetters['workspaces/currentWorkspace']?.id) > -1;
33 | });
34 | },
35 | },
36 | mutations: {
37 | setItems(state, payload) {
38 | state.items = convertKeysToCamelCase(payload);
39 | },
40 | },
41 | }
42 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/store/workspaces.js:
--------------------------------------------------------------------------------
1 | import makeRequestStore from '../../../core/utils/makeRequestStore';
2 | import convertKeysToCamelCase from '../../../core/utils/convertKeysToCamelCase';
3 |
4 | import {
5 | getWorkspaces,
6 | getWorkspacesIncludeInactive,
7 | createWorkspace,
8 | updateWorkspace,
9 | deleteWorkspace,
10 | } from '../services/workspaces';
11 |
12 | const modules = [
13 | { getWorkspaces },
14 | { getWorkspacesIncludeInactive },
15 | { createWorkspace },
16 | { updateWorkspace },
17 | { deleteWorkspace },
18 | ];
19 |
20 | export default {
21 | namespaced: true,
22 | modules: {
23 | ...modules.reduce((acc, module) => ({
24 | ...acc,
25 | ...makeRequestStore(module),
26 | }), {}),
27 | },
28 | state: {
29 | items: [],
30 | selectedWorkspace: null,
31 | },
32 | getters: {
33 | currentWorkspace(state) {
34 | //TODO converKeysToCamelCase deeper
35 | let copy = _.clone(state.selectedWorkspace);
36 | if(!copy) return copy;
37 | copy.settings = convertKeysToCamelCase(copy.settings);
38 | return copy;
39 | },
40 | },
41 | mutations: {
42 | setItems(state, payload) {
43 | state.items = convertKeysToCamelCase(payload);
44 | },
45 | setSelectedWorkspace(state, workspace) {
46 | state.selectedWorkspace = workspace;
47 | },
48 | },
49 | }
50 |
--------------------------------------------------------------------------------
/resources/js/modules/settings/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | Membros
8 |
9 |
10 |
14 | Workspaces
15 |
16 |
17 |
21 | Times
22 |
23 |
24 |
28 | Categorias
29 |
30 |
31 |
35 | Categorias de backlog
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/resources/js/vuetify.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuetify from 'vuetify';
3 | import 'vuetify/dist/vuetify.min.css';
4 |
5 | Vue.use(Vuetify);
6 |
7 | const opts = {
8 | icons: {
9 | iconfont: 'mdiSvg', // 'mdi' || 'mdiSvg' || 'md' || 'fa' || 'fa4' || 'faSvg'
10 | },
11 | theme: {
12 | themes: {
13 | light: {
14 | primary: '#399AE7',
15 | },
16 | dark: {
17 | primary: '#399AE7',
18 | },
19 | },
20 | },
21 | };
22 |
23 | export default new Vuetify(opts);
24 |
--------------------------------------------------------------------------------
/resources/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/resources/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/resources/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Passwords must be at least eight characters and match the confirmation.',
17 | 'reset' => 'Your password has been reset!',
18 | 'sent' => 'We have e-mailed your password reset link!',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that e-mail address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/resources/sass/_variables.scss:
--------------------------------------------------------------------------------
1 | // Body
2 | $body-bg: #f8fafc;
3 |
4 | // Typography
5 | $font-family-sans-serif: 'Nunito', sans-serif;
6 | $font-size-base: 0.9rem;
7 | $line-height-base: 1.6;
8 |
9 | // Colors
10 | $blue: #3490dc;
11 | $indigo: #6574cd;
12 | $purple: #9561e2;
13 | $pink: #f66d9b;
14 | $red: #e3342f;
15 | $orange: #f6993f;
16 | $yellow: #ffed4a;
17 | $green: #38c172;
18 | $teal: #4dc0b5;
19 | $cyan: #6cb2eb;
20 |
--------------------------------------------------------------------------------
/resources/sass/app.scss:
--------------------------------------------------------------------------------
1 | .task-card {
2 | box-shadow: 0 3px 1px -2px rgba(0,0,0,.05),
3 | 0 2px 2px 0 rgba(0,0,0,.05),
4 | 0 1px 5px 0 rgba(0,0,0,.05) !important;
5 | max-width: 250px;
6 | min-width: 250px;
7 | }
8 |
9 | .v-tabs-items {
10 | background-color: transparent!important;
11 | }
12 |
13 | .v-expansion-panel-content__wrap {
14 | padding: 0!important;
15 | }
16 |
17 | .v-expansion-panel {
18 | background: transparent!important;
19 | }
20 |
21 | .v-expansion-panel-header {
22 | padding: 0!important;
23 | }
24 |
25 | .v-expansion-panel--active>.v-expansion-panel-header {
26 | min-height: 48px!important;
27 | }
28 |
29 | .v-expansion-panel--active+.v-expansion-panel, .v-expansion-panel--active:not(:first-child) {
30 | margin-top: 0px!important;
31 | }
32 |
33 | .swal2-popup {
34 | font-family: Poppins, sans-serif !important;
35 | }
36 |
37 | .v-data-table tbody tr.v-data-table__expanded__content {
38 | box-shadow: unset !important;
39 | }
--------------------------------------------------------------------------------
/resources/sass/home.scss:
--------------------------------------------------------------------------------
1 |
2 | // Variables
3 | @import '_variables';
4 |
5 | // Bootstrap
6 | @import '~bootstrap/scss/bootstrap';
7 |
--------------------------------------------------------------------------------
/resources/views/auth/verify.blade.php:
--------------------------------------------------------------------------------
1 | @extends('layouts.app')
2 |
3 | @section('content')
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | @if (session('resent'))
12 |
13 | {{ __('A fresh verification link has been sent to your email address.') }}
14 |
15 | @endif
16 |
17 | {{ __('Before proceeding, please check your email for a verification link.') }}
18 | {{ __('If you did not receive the email') }},
19 |
23 |
24 |
25 |
26 |
27 |
28 | @endsection
29 |
--------------------------------------------------------------------------------
/resources/views/index.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | @php
4 | $addons = auth()->user()->member->company['addons'] ?? [];
5 | @endphp
6 |
7 | @yield('title')
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | @foreach($addons as $addon)
16 | @if(isset($addon['styles']))
17 | @foreach($addon['styles'] as $style)
18 |
19 | @endforeach
20 | @endif
21 | @endforeach
22 |
23 |
24 |
25 |
26 |
27 | @foreach($addons as $addon)
28 | @if(isset($addon['scripts']))
29 | @foreach($addon['scripts'] as $script)
30 |
31 | @endforeach
32 | @endif
33 | @endforeach
34 |
35 |
36 |
--------------------------------------------------------------------------------
/resources/views/layouts/app.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ config('app.name', 'Laravel') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
29 |
30 |
31 | @yield('content')
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/resources/views/vendor/emails/welcome.blade.php:
--------------------------------------------------------------------------------
1 | @component('mail::message')
2 |
3 |
4 | Eae {{ $name }},
5 |
6 |
7 |
8 | Seu email foi cadastrado no {{ config('app.name') }}
9 | Clique no botão abaixo para definir sua senha para acessar a plataforma.
10 |
11 |
12 | @component('mail::button', ['url' => $url])
13 | Cadastrar senha
14 | @endcomponent
15 |
16 |
17 |
18 | Atenciosamente, Sysvale Team .
19 |
20 |
21 | @endcomponent
22 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/button.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/footer.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/header.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/message.blade.php:
--------------------------------------------------------------------------------
1 | @component('mail::layout')
2 | {{-- Header --}}
3 | @slot('header')
4 | @component('mail::header', ['url' => config('app.url')])
5 | Trelássio
6 | @endcomponent
7 | @endslot
8 |
9 | {{-- Body --}}
10 | {{ $slot }}
11 |
12 | {{-- Subcopy --}}
13 | @isset($subcopy)
14 | @slot('subcopy')
15 | @component('mail::subcopy')
16 | {{ $subcopy }}
17 | @endcomponent
18 | @endslot
19 | @endisset
20 |
21 | {{-- Footer --}}
22 | @slot('footer')
23 | @component('mail::footer')
24 | © {{ date('Y') }} Trelássio . Todos os direitos reservados.
25 | @endcomponent
26 | @endslot
27 | @endcomponent
28 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/panel.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ Illuminate\Mail\Markdown::parse($slot) }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/promotion.blade.php:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/promotion/button.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/subcopy.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ Illuminate\Mail\Markdown::parse($slot) }}
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/html/table.blade.php:
--------------------------------------------------------------------------------
1 |
2 | {{ Illuminate\Mail\Markdown::parse($slot) }}
3 |
4 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/button.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}: {{ $url }}
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/footer.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/header.blade.php:
--------------------------------------------------------------------------------
1 | [{{ $slot }}]({{ $url }})
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/layout.blade.php:
--------------------------------------------------------------------------------
1 | {!! strip_tags($header) !!}
2 |
3 | {!! strip_tags($slot) !!}
4 | @isset($subcopy)
5 |
6 | {!! strip_tags($subcopy) !!}
7 | @endisset
8 |
9 | {!! strip_tags($footer) !!}
10 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/message.blade.php:
--------------------------------------------------------------------------------
1 | @component('mail::layout')
2 | {{-- Header --}}
3 | @slot('header')
4 | @component('mail::header', ['url' => config('app.url')])
5 | {{ config('app.name') }}
6 | @endcomponent
7 | @endslot
8 |
9 | {{-- Body --}}
10 | {{ $slot }}
11 |
12 | {{-- Subcopy --}}
13 | @isset($subcopy)
14 | @slot('subcopy')
15 | @component('mail::subcopy')
16 | {{ $subcopy }}
17 | @endcomponent
18 | @endslot
19 | @endisset
20 |
21 | {{-- Footer --}}
22 | @slot('footer')
23 | @component('mail::footer')
24 | © {{ date('Y') }} Sysvale. Todos os direitos reservados.
25 | @endcomponent
26 | @endslot
27 | @endcomponent
28 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/panel.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/promotion.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/promotion/button.blade.php:
--------------------------------------------------------------------------------
1 | [{{ $slot }}]({{ $url }})
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/subcopy.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}
2 |
--------------------------------------------------------------------------------
/resources/views/vendor/mail/text/table.blade.php:
--------------------------------------------------------------------------------
1 | {{ $slot }}
2 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', function (Request $request) {
19 | return $request->user();
20 | });
21 |
22 | Route::post('cards', [CardController::class, 'store'])
23 | ->middleware('client')
24 | ->name('api.cards.store');
25 |
26 | Route::post('cards/batch', [CardController::class, 'handleBatch'])
27 | ->middleware('client')
28 | ->name('api.cards.batch');
29 |
--------------------------------------------------------------------------------
/routes/channels.php:
--------------------------------------------------------------------------------
1 | id === (int) $id;
16 | });
17 |
--------------------------------------------------------------------------------
/routes/console.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
18 | })->describe('Display an inspiring quote');
19 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $uri = urldecode(
11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
12 | );
13 |
14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the
15 | // built-in PHP web server. This provides a convenient way to test a Laravel
16 | // application without having installed a "real" web server software here.
17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
18 | return false;
19 | }
20 |
21 | require_once __DIR__.'/public/index.php';
22 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 | routes.php
3 | schedule-*
4 | compiled.php
5 | services.json
6 | events.scanned.php
7 | routes.scanned.php
8 | down
9 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/tests/CreatesApplication.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
19 |
20 | return $app;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Feature/App/Console/Commands/CompanyClientCommandTest.php:
--------------------------------------------------------------------------------
1 | create();
15 |
16 | $this->artisan('passport:company', ['company_id' => $company->id])->assertExitCode(0);
17 |
18 | $this->assertDatabaseHas('company_clients', ['company_id' => $company->id]);
19 |
20 | $company_client = CompanyClient::whereCompanyId($company->id)->first();
21 | $this->assertTrue($company->is($company_client->company));
22 | $this->assertNotNull($company_client->client_id);
23 | $this->assertInstanceOf(Client::class, $company_client->client);
24 | }
25 |
26 | public function testDoNotCreateNewClientForCompanyThatAlreadyHasClient(): void
27 | {
28 | $company_client = CompanyClient::create([
29 | 'company_id' => factory(Company::class)->create()->id,
30 | 'client_id' => Client::create([
31 | 'id' => 'client_id',
32 | 'secret' => 'client_secret',
33 | ])->id,
34 | ]);
35 |
36 | $this->artisan('passport:company', ['company_id' => $company_client->company_id])
37 | ->assertExitCode(0);
38 |
39 | $this->assertEquals(1, CompanyClient::whereCompanyId($company_client->company_id)->count());
40 | }
41 |
42 | public function testShowClientIdAndClientSecret(): void
43 | {
44 | $client = Client::create([
45 | 'secret' => 'client_secret',
46 | ]);
47 |
48 | $company_client = CompanyClient::create([
49 | 'company_id' => factory(Company::class)->create()->id,
50 | 'client_id' => $client->id,
51 | ]);
52 |
53 | $this->artisan('passport:company', ['company_id' => $company_client->company_id])
54 | ->expectsOutput('Client ID: ' . $client->id)
55 | ->expectsOutput('Client Secret: client_secret')
56 | ->assertExitCode(0);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/Feature/App/Http/Controllers/MemberControllerTest.php:
--------------------------------------------------------------------------------
1 | user = factory(User::class)->state('with-member-company')->create();
16 | $this->actingAs($this->user);
17 | }
18 |
19 | public function testIfAuthenticatedMemberCanCreateANewMember()
20 | {
21 | $member = factory(Member::class)->state('with-company')->make();
22 | $data = $member->toArray();
23 |
24 | $data = array_add($data, 'email', 'teste@email.com');
25 | $data = array_add($data, 'team_ids', [1]);
26 |
27 | $response = $this->post('members', $data);
28 |
29 | $this->assertEquals(2, Member::all()->count());
30 | $this->assertEquals(201, $response->status());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Feature/App/Http/Controllers/TeamControllerTest.php:
--------------------------------------------------------------------------------
1 | user = factory(User::class)->state('with-member-company')->create();
16 | factory(Team::class, 3)->state('with-company')->make();
17 |
18 | $this->actingAs($this->user);
19 | }
20 |
21 | public function testIfAuthenticatedMemberCanCreateANewTeam()
22 | {
23 | $workspace = factory(Team::class)->state('with-company')->make();
24 | $data = $workspace->toArray();
25 |
26 | $response = $this->post('teams', $data);
27 |
28 | $this->assertEquals(1, Team::all()->count());
29 | $this->assertEquals(201, $response->status());
30 | }
31 |
32 | public function testIfIsGettingOnlyAuthenticatedMemberCompanyTeam()
33 | {
34 | factory(Team::class, 1)->create([
35 | 'company_id' => $this->user->member->company_id,
36 | ]);
37 |
38 | $response = $this->get('teams');
39 |
40 | $this->assertCount(1, json_decode($response->getContent()));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Feature/App/Http/Controllers/WorkspaceControllerTest.php:
--------------------------------------------------------------------------------
1 | user = factory(User::class)->state('with-member-company')->create();
18 | factory(Workspace::class, 2)->state('with-company')->create();
19 |
20 | $this->actingAs($this->user);
21 | }
22 |
23 | public function testIfAuthenticatedMemberCanCreateANewWorkspace()
24 | {
25 | $this->actingAs($this->user);
26 |
27 | $workspace = factory(Workspace::class)->state('with-company')->make();
28 | $data = $workspace->toArray();
29 |
30 | $response = $this->post('workspaces', $data);
31 |
32 | $this->assertEquals(1, Workspace::all()->count());
33 | $this->assertEquals(201, $response->status());
34 | }
35 |
36 | public function testIfAuthenticatedMemberCanNotAccessAWorkspaceInDifferentCompany()
37 | {
38 | $response = $this->get('workspaces');
39 | $this->assertCount(0, json_decode($response->getContent()));
40 | }
41 |
42 | public function testIfIsGettingOnlyAuthenticatedMemberWorkspace()
43 | {
44 | factory(Workspace::class, 2)->create([
45 | 'company_id' => $this->user->member->company_id,
46 | ]);
47 |
48 | $response = $this->get('workspaces');
49 |
50 | $this->assertCount(2, json_decode($response->getContent()));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Feature/App/Observers/CardObserverTest.php:
--------------------------------------------------------------------------------
1 | state('with-member-company')->create();
21 | Auth::login($user);
22 | request()->merge(
23 | app(ResolveCompanyTenant::class)->handle(request(), fn ($request) => $request)->all()
24 | );
25 |
26 | $card = factory(Card::class)->create();
27 |
28 | $this->assertEquals($user->id, $card->user_id);
29 | $this->assertTrue($user->member->company->is($card->company));
30 | }
31 |
32 | public function testLinkCompanyAndNullUserWhenCreatingCardUsingClient(): void
33 | {
34 | $company = factory(Company::class)->create();
35 | $client = Client::create();
36 | CompanyClient::create([
37 | 'company_id' => $company->id,
38 | 'client_id' => $client->id,
39 | ]);
40 |
41 | Passport::actingAsClient($client);
42 | $this->mock(Parser::class, function ($mock) {
43 | $mock->shouldReceive('parse->claims->get')->andReturn('access_token');
44 | });
45 | request()->headers->add(['Authorization' => 'Bearer access_token']);
46 | request()->merge(
47 | app(ResolveCompanyTenant::class)->handle(request(), fn ($request) => $request)->all()
48 | );
49 |
50 | $card = factory(Card::class)->create();
51 |
52 | $this->assertNull($card->user_id);
53 | $this->assertEquals($company->id, $card->company_id);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | clearDB('mongodb');
20 |
21 | parent::tearDown();
22 | }
23 |
24 | private function clearDB($connection): void
25 | {
26 | foreach (DB::connection($connection)->listCollections() as $coll) {
27 | DB::connection($connection)
28 | ->table($coll->getName())
29 | ->delete();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Unit/ExampleTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/webpack.mix.js:
--------------------------------------------------------------------------------
1 | const mix = require('laravel-mix');
2 |
3 | /*
4 | |--------------------------------------------------------------------------
5 | | Mix Asset Management
6 | |--------------------------------------------------------------------------
7 | |
8 | | Mix provides a clean, fluent API for defining some Webpack build steps
9 | | for your Laravel application. By default, we are compiling the Sass
10 | | file for the application as well as bundling up all the JS files.
11 | |
12 | */
13 |
14 | module.exports = {
15 | rules: [
16 | {
17 | test: /\.s(c|a)ss$/,
18 | use: [
19 | 'vue-style-loader',
20 | 'css-loader',
21 | {
22 | loader: 'sass-loader',
23 | // Requires sass-loader@^7.0.0
24 | options: {
25 | implementation: require('sass'),
26 | fiber: require('fibers'),
27 | indentedSyntax: true // optional
28 | },
29 | // Requires sass-loader@^8.0.0
30 | options: {
31 | implementation: require('sass'),
32 | sassOptions: {
33 | fiber: require('fibers'),
34 | indentedSyntax: true // optional
35 | },
36 | },
37 | },
38 | ],
39 | },
40 | ],
41 | }
42 |
43 | mix.babelConfig({
44 | plugins: ['@babel/plugin-syntax-dynamic-import'],
45 | });
46 |
47 | mix.js('resources/js/modules/board/app.js', 'public/js/app.min.js')
48 | .sass('resources/sass/app.scss', 'public/css')
49 | .sass('resources/sass/home.scss', 'public/css');
50 |
51 | mix.version();
52 |
--------------------------------------------------------------------------------