Compare commits
203 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5634d4d232 | ||
|
|
9a1a07307b | ||
|
|
8410447a29 | ||
|
|
f0fb7a26f3 | ||
|
|
f55ca959b8 | ||
|
|
d622f543e9 | ||
|
|
1f1c684008 | ||
|
|
e8c5472d45 | ||
|
|
4ff19d9277 | ||
|
|
7dfd80cf23 | ||
|
|
e19d810a2f | ||
|
|
ab998c975d | ||
|
|
9706922312 | ||
|
|
ebb52b5968 | ||
|
|
6c783247a7 | ||
|
|
6cf790feab | ||
|
|
57c7613455 | ||
|
|
eddeb4f7f6 | ||
|
|
d9d1fea624 | ||
|
|
22d79cfb11 | ||
|
|
22bc49baab | ||
|
|
adb2a8b21c | ||
|
|
7246d7894d | ||
|
|
10b75031e7 | ||
|
|
68a48fdf5a | ||
|
|
a7debacd65 | ||
|
|
dd10d4ba3a | ||
|
|
a7bd6d6420 | ||
|
|
8cfc409d5c | ||
|
|
bc7a1020bc | ||
|
|
e828c8bf66 | ||
|
|
f28dbdd1df | ||
|
|
965226fae6 | ||
|
|
bd9b7cd32e | ||
|
|
8c67989526 | ||
|
|
2212b0ae08 | ||
|
|
c2b25c3503 | ||
|
|
f106c17588 | ||
|
|
8e9d117918 | ||
|
|
0dbb483fc4 | ||
|
|
980717f37c | ||
|
|
020fcdaa03 | ||
|
|
095f80eac1 | ||
|
|
6a973703c4 | ||
|
|
9a853088b1 | ||
|
|
0ec3be521d | ||
|
|
6854af708b | ||
|
|
8f1c920e26 | ||
|
|
efe5e8ace6 | ||
|
|
91f4773c60 | ||
|
|
6b5b4ec619 | ||
|
|
4edd92e572 | ||
|
|
da03fbe273 | ||
|
|
b7f7b7c55d | ||
|
|
49d37a8fa6 | ||
|
|
195a5fcb3e | ||
|
|
e0af4a243a | ||
|
|
0c4d376c22 | ||
|
|
6fabb998ed | ||
|
|
04bd156849 | ||
|
|
a43037d2f9 | ||
|
|
ef57fe9fe5 | ||
|
|
62901a015e | ||
|
|
cbabb40f83 | ||
|
|
870986faa4 | ||
|
|
33ea75a5ed | ||
|
|
3e09baf6f3 | ||
|
|
d5f1891764 | ||
|
|
098e40576e | ||
|
|
18b66793a0 | ||
|
|
7ee8a2be82 | ||
|
|
87cc2dec86 | ||
|
|
ea41e19724 | ||
|
|
65772aeb1d | ||
|
|
ea6ac998d8 | ||
|
|
9bc3d9e5e0 | ||
|
|
f99dac8aab | ||
|
|
3f4342f3bb | ||
|
|
481c732f3e | ||
|
|
887837dcaf | ||
|
|
4bdcf60dfa | ||
|
|
435d5626ee | ||
|
|
50ba8eaf8a | ||
|
|
f2a65c85b0 | ||
|
|
1a2fe21212 | ||
|
|
d702c02bdb | ||
|
|
b1a6dfb72c | ||
|
|
844928e945 | ||
|
|
3560e6c7f6 | ||
|
|
aa3f507483 | ||
|
|
8a34a30ab4 | ||
|
|
4a0c556937 | ||
|
|
085028a465 | ||
|
|
f9ed83ff60 | ||
|
|
1ffb5e5bdd | ||
|
|
c6fcd7cfa6 | ||
|
|
3d81cd77ce | ||
|
|
fba178b598 | ||
|
|
1f02f86610 | ||
|
|
efb3914e4f | ||
|
|
b0f4c37153 | ||
|
|
25ab578c93 | ||
|
|
52386bbfd4 | ||
|
|
d6ed7c1f04 | ||
|
|
5477affb97 | ||
|
|
3e199d0f68 | ||
|
|
7564600ace | ||
|
|
ba6f7f8e7b | ||
|
|
f1eddb1cc2 | ||
|
|
658fedd3eb | ||
|
|
31510149af | ||
|
|
fb8720d41b | ||
|
|
08afe201f8 | ||
|
|
afd8f09d02 | ||
|
|
33cf4f1c93 | ||
|
|
e1944924c1 | ||
|
|
3a0cc47fa8 | ||
|
|
3a2b5a0af9 | ||
|
|
ca7057ce77 | ||
|
|
0dd283b68d | ||
|
|
7c028a4505 | ||
|
|
b21dd4d260 | ||
|
|
4838b54b4c | ||
|
|
911ee5df4b | ||
|
|
bbfd4360f2 | ||
|
|
3a51ccb0f3 | ||
|
|
1c3f20782b | ||
|
|
673494aeda | ||
|
|
1bd04e2e52 | ||
|
|
aa17aab45f | ||
|
|
ec47d2610f | ||
|
|
560ef30cd8 | ||
|
|
b2908aac9f | ||
|
|
0da4a6fa13 | ||
|
|
b6720bae3d | ||
|
|
a58bf2ff3b | ||
|
|
2b94d780b6 | ||
|
|
74edc5c785 | ||
|
|
781161c5e4 | ||
|
|
2d70ff9f29 | ||
|
|
17a01e1f7a | ||
|
|
e3d828a9e5 | ||
|
|
d17678342e | ||
|
|
b646cb41fe | ||
|
|
5bd7673a18 | ||
|
|
2faf043f68 | ||
|
|
66c889b02a | ||
|
|
74600ef3a7 | ||
|
|
35e18112b9 | ||
|
|
f10b46a012 | ||
|
|
523715a3c1 | ||
|
|
c8b4f97c88 | ||
|
|
65b92edc99 | ||
|
|
2ee245be4c | ||
|
|
766a990361 | ||
|
|
a23509fa09 | ||
|
|
6c8cc25bae | ||
|
|
3c529a7bc0 | ||
|
|
c8d179e7b3 | ||
|
|
8e4d6d4198 | ||
|
|
a3c2697c86 | ||
|
|
c6d89101b4 | ||
|
|
ad43378b52 | ||
|
|
f40f2e2aa5 | ||
|
|
b55d812aaa | ||
|
|
ab08bad87e | ||
|
|
e8b920a8cd | ||
|
|
cb779c83b8 | ||
|
|
a393b24392 | ||
|
|
cd89f6b443 | ||
|
|
d9b316eae4 | ||
|
|
722369cf61 | ||
|
|
0cc315d2ef | ||
|
|
7559a47044 | ||
|
|
55fef3421e | ||
|
|
97d8111f4b | ||
|
|
4354f6a445 | ||
|
|
abdbbae843 | ||
|
|
f150e96d39 | ||
|
|
d2324145a4 | ||
|
|
b6c74efd3d | ||
|
|
57a437db48 | ||
|
|
f3cb8e7974 | ||
|
|
91c1fe5532 | ||
|
|
29a837ff61 | ||
|
|
bbe4478c09 | ||
|
|
28ee9f593d | ||
|
|
eb2d4e5267 | ||
|
|
c19d5829e6 | ||
|
|
c1fb743e34 | ||
|
|
1d07c93799 | ||
|
|
32cb11622c | ||
|
|
911d7a2f6d | ||
|
|
32be1746b5 | ||
|
|
a5d4c58604 | ||
|
|
0d6f34b05a | ||
|
|
606ca09608 | ||
|
|
6523875860 | ||
|
|
83b7e42b90 | ||
|
|
6f20a6b8db | ||
|
|
88f3da4c92 | ||
|
|
bd3133f06c | ||
|
|
bb2cbc5ad8 |
894 changed files with 147379 additions and 45809 deletions
5
.browserslistrc
Normal file
5
.browserslistrc
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Browsers that we support
|
||||||
|
|
||||||
|
last 3 version
|
||||||
|
> 0.3%
|
||||||
|
not dead # no browsers without security updates
|
||||||
|
|
@ -1,10 +1,2 @@
|
||||||
.git/
|
.git
|
||||||
.gitlab/
|
|
||||||
.gitlab-ci.yml
|
|
||||||
*.md
|
*.md
|
||||||
.vscode/
|
|
||||||
auth.json
|
|
||||||
Dockerfile
|
|
||||||
manifest.yaml
|
|
||||||
node_modules/
|
|
||||||
production-resources.yaml
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,5 @@ indent_size = 2
|
||||||
[docker-compose.yml]
|
[docker-compose.yml]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.scss]
|
[*.vue]
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.{tsx,ts,cjs,js}]
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
|
||||||
88
.env.example
88
.env.example
|
|
@ -1,65 +1,47 @@
|
||||||
# APP
|
APP_NAME=Laravel
|
||||||
APP_NAME=
|
APP_ENV=local
|
||||||
APP_ENV=
|
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=
|
APP_DEBUG=true
|
||||||
APP_URL=
|
APP_URL=http://localhost
|
||||||
APP_LOCALE=
|
|
||||||
APP_FALLBACK_LOCALE=
|
LOG_CHANNEL=stack
|
||||||
APP_FAKER_LOCALE=
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
# DATABASE
|
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST=
|
DB_HOST=127.0.0.1
|
||||||
DB_PORT=
|
DB_PORT=3306
|
||||||
DB_DATABASE=
|
DB_DATABASE=laravel
|
||||||
DB_USERNAME=
|
DB_USERNAME=root
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
|
|
||||||
# MAILS
|
|
||||||
MAIL_MAILER=smtp
|
|
||||||
MAIL_HOST=
|
|
||||||
MAIL_PORT=
|
|
||||||
MAIL_USERNAME=
|
|
||||||
MAIL_PASSWORD=
|
|
||||||
MAIL_ENCRYPTION=
|
|
||||||
MAIL_FROM_ADDRESS=
|
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
|
||||||
|
|
||||||
# SENTRY
|
|
||||||
SENTRY_LARAVEL_DSN=
|
|
||||||
|
|
||||||
# ADMIN SECURE
|
|
||||||
LDC_WEB_API_ENDPOINT=
|
|
||||||
LDC_WEB_API_KEY=
|
|
||||||
LDC_WEB_CIDR_MANUAL_ALLOWED_LIST=
|
|
||||||
|
|
||||||
## TELESCOPE
|
|
||||||
TELESCOPE_ENABLED=true
|
|
||||||
|
|
||||||
# REDIS
|
|
||||||
REDIS_HOST=
|
|
||||||
REDIS_PORT=
|
|
||||||
REDIS_USERNAME=
|
|
||||||
REDIS_PASSWORD=
|
|
||||||
REDIS_PREFIX=
|
|
||||||
REDIS_SCHEME=
|
|
||||||
|
|
||||||
# OTHERS
|
|
||||||
LOG_CHANNEL=
|
|
||||||
LOG_DEPRECATIONS_CHANNEL=
|
|
||||||
LOG_LEVEL=
|
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
BROADCAST_DRIVER=log
|
||||||
|
|
||||||
CACHE_DRIVER=file
|
CACHE_DRIVER=file
|
||||||
CACHE_STORE=file
|
|
||||||
|
|
||||||
FILESYSTEM_DISK=local
|
FILESYSTEM_DISK=local
|
||||||
|
|
||||||
QUEUE_CONNECTION=sync
|
QUEUE_CONNECTION=sync
|
||||||
|
|
||||||
SESSION_DRIVER=file
|
SESSION_DRIVER=file
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=smtp.mailtrap.io
|
||||||
|
MAIL_PORT=2525
|
||||||
|
MAIL_USERNAME=null
|
||||||
|
MAIL_PASSWORD=null
|
||||||
|
MAIL_ENCRYPTION=null
|
||||||
|
MAIL_FROM_ADDRESS=noreply-sw@ldc.fr
|
||||||
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
MAIL_LIST_SERVICEWEB=digital@ldc.fr
|
||||||
|
MAIL_ADMIN=digital@ldc.fr
|
||||||
|
|
||||||
|
# Sentry
|
||||||
|
SENTRY_LARAVEL_DSN=
|
||||||
|
|
||||||
|
# UWIL ADMIN SECURE
|
||||||
|
# - LIST SEPARATED WITH ',' (Required)
|
||||||
|
UWIL_ADMIN_SECURE_CIDR_LIST=""
|
||||||
|
# - LIST SEPARATED WITH ',' (Optional)
|
||||||
|
UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST=
|
||||||
|
|
||||||
|
# GITLAB API
|
||||||
|
GITLAB_URL=
|
||||||
|
GITLAB_TOKEN=
|
||||||
|
|
|
||||||
55
.eslintrc.json
Normal file
55
.eslintrc.json
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:vue/vue3-strongly-recommended",
|
||||||
|
"@vue/eslint-config-typescript"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.vue"],
|
||||||
|
"rules": {
|
||||||
|
"no-undef": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.vue"],
|
||||||
|
"rules": {
|
||||||
|
"indent": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parser": "vue-eslint-parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["vue", "@typescript-eslint"],
|
||||||
|
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
|
||||||
|
"rules": {
|
||||||
|
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"quotes": ["error", "double"],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
|
||||||
|
"vue/one-component-per-file": "off"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"node": {
|
||||||
|
"extensions": [".js", ".vue", ".ts", ".d.ts"]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"extensions": [".vue", ".js", ".ts", ".scss", ".d.ts"],
|
||||||
|
"map": [
|
||||||
|
["~", "./resources/assets"],
|
||||||
|
["@", "./node_modules"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
.gitignore
vendored
50
.gitignore
vendored
|
|
@ -1,38 +1,28 @@
|
||||||
*.log
|
|
||||||
.env
|
.env
|
||||||
.env.backup
|
.env.backup
|
||||||
.env.example
|
|
||||||
.env.production
|
.env.production
|
||||||
/auth.json
|
.phpunit.result.cache
|
||||||
/.npmrc
|
.stylelintcache
|
||||||
/bootstrap/ssr
|
/.fleet
|
||||||
|
/.idea
|
||||||
|
/.phpunit.cache
|
||||||
|
/.vscode
|
||||||
/node_modules
|
/node_modules
|
||||||
/public/build
|
/public/build
|
||||||
|
/public/cache
|
||||||
|
/public/hot
|
||||||
|
/public/sitemap.xml
|
||||||
/public/storage
|
/public/storage
|
||||||
/public/favicons-back
|
|
||||||
/public/android-icon-36x36.png
|
|
||||||
/public/android-icon-48x48.png
|
|
||||||
/public/android-icon-72x72.png
|
|
||||||
/public/android-icon-96x96.png
|
|
||||||
/public/android-icon-144x144.png
|
|
||||||
/public/android-icon-192x192.png
|
|
||||||
/public/apple-icon-57x57.png
|
|
||||||
/public/apple-icon-60x60.png
|
|
||||||
/public/apple-icon-72x72.png
|
|
||||||
/public/apple-icon-76x76.png
|
|
||||||
/public/apple-icon-114x114.png
|
|
||||||
/public/apple-icon-120x120.png
|
|
||||||
/public/apple-icon-144x144.png
|
|
||||||
/public/apple-icon-152x152.png
|
|
||||||
/public/apple-icon-180x180.png
|
|
||||||
/public/favicon-16x16.png
|
|
||||||
/public/favicon-32x32.png
|
|
||||||
/public/favicon.ico
|
|
||||||
/public/manifest.json
|
|
||||||
/public/ms-icon-70x70.png
|
|
||||||
/public/ms-icon-144x144.png
|
|
||||||
/public/ms-icon-150x150.png
|
|
||||||
/public/ms-icon-310x310.png
|
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
/storage/pail
|
|
||||||
/vendor
|
/vendor
|
||||||
|
auth.json
|
||||||
|
docker-compose.override.yml
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
storage/samlidp/*
|
||||||
|
.npmrc
|
||||||
|
.zshrc
|
||||||
|
.npm/
|
||||||
|
.composer/
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
include:
|
include:
|
||||||
- project: "ldc-web/shared-ci"
|
- project: 'ldc-web/shared-ci'
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
file: "lint/lint.yml"
|
file: 'lint/lint.yml'
|
||||||
- project: "ldc-web/shared-ci"
|
- project: 'ldc-web/shared-ci'
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
file: "security/security.yml"
|
file: 'security/security.yml'
|
||||||
- project: "ldc-web/shared-ci"
|
- project: 'ldc-web/shared-ci'
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
file: "build/docker-multistage.yml"
|
file: "build/docker-multistage.yml"
|
||||||
- project: "ldc-web/shared-ci"
|
- project: 'ldc-web/shared-ci'
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
file: "test/test.yml"
|
file: 'test/test.yml'
|
||||||
- project: "ldc-web/shared-ci"
|
- project: 'ldc-web/shared-ci'
|
||||||
ref: "main"
|
ref: 'main'
|
||||||
file: "deploy/deploy.yml"
|
file: 'deploy/deploy.yml'
|
||||||
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- lint
|
- lint
|
||||||
- security
|
- security
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
npx --no -- commitlint --config node_modules/@uwil/husky-config/commitlint.config.cjs --edit $1
|
|
||||||
5
.npmrc
5
.npmrc
|
|
@ -1,5 +0,0 @@
|
||||||
@ldc-web:registry=https://lab.s-web.fr/api/v4/packages/npm/
|
|
||||||
//lab.s-web.fr/api/v4/packages/npm/:_authToken=glpat-Td7iwL19ZszGUMosPyNQ
|
|
||||||
|
|
||||||
@uwil:registry=https://lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/
|
|
||||||
//lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/:_authToken=glpat-Td7iwL19ZszGUMosPyNQ
|
|
||||||
|
|
@ -4,8 +4,13 @@
|
||||||
"stylelint-config-sass-guidelines",
|
"stylelint-config-sass-guidelines",
|
||||||
"stylelint-config-idiomatic-order"
|
"stylelint-config-idiomatic-order"
|
||||||
],
|
],
|
||||||
"plugins": ["stylelint-scss"],
|
"plugins": [
|
||||||
"files": ["**/*.css", "**/*.scss"],
|
"stylelint-scss"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"**/*.css",
|
||||||
|
"**/*.scss"
|
||||||
|
],
|
||||||
"ignoreFiles": [
|
"ignoreFiles": [
|
||||||
"**/vendor/*.css",
|
"**/vendor/*.css",
|
||||||
"**/vendor/**/*.css",
|
"**/vendor/**/*.css",
|
||||||
|
|
@ -13,12 +18,8 @@
|
||||||
"**/vendor/**/*.scss",
|
"**/vendor/**/*.scss",
|
||||||
"**/vendor/*.sass",
|
"**/vendor/*.sass",
|
||||||
"**/vendor/**/*.sass",
|
"**/vendor/**/*.sass",
|
||||||
"**/packages/*.css",
|
"**/vendor/*.less",
|
||||||
"**/packages/**/*.css",
|
"**/vendor/**/*.less"
|
||||||
"**/packages/*.scss",
|
|
||||||
"**/packages/**/*.scss",
|
|
||||||
"**/packages/*.sass",
|
|
||||||
"**/packages/**/*.sass"
|
|
||||||
],
|
],
|
||||||
"customSyntax": "postcss-scss",
|
"customSyntax": "postcss-scss",
|
||||||
"rules": {
|
"rules": {
|
||||||
|
|
@ -28,6 +29,8 @@
|
||||||
"order/properties-alphabetical-order": null,
|
"order/properties-alphabetical-order": null,
|
||||||
"max-nesting-depth": 5,
|
"max-nesting-depth": 5,
|
||||||
"selector-max-compound-selectors": 5,
|
"selector-max-compound-selectors": 5,
|
||||||
|
"declaration-colon-newline-after": null,
|
||||||
|
"value-list-comma-newline-after": null,
|
||||||
"scss/double-slash-comment-empty-line-before": [
|
"scss/double-slash-comment-empty-line-before": [
|
||||||
"always",
|
"always",
|
||||||
{
|
{
|
||||||
|
|
@ -37,20 +40,6 @@
|
||||||
"inside-block"
|
"inside-block"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"at-rule-no-unknown": null,
|
|
||||||
"scss/at-rule-no-unknown": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"ignoreAtRules": [
|
|
||||||
"tailwind",
|
|
||||||
"apply",
|
|
||||||
"variants",
|
|
||||||
"responsive",
|
|
||||||
"screen",
|
|
||||||
"layer"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
.vscode/extensions.json
vendored
19
.vscode/extensions.json
vendored
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"rvest.vs-code-prettier-eslint",
|
|
||||||
"onecentlin.laravel-extension-pack",
|
|
||||||
"johnpapa.vscode-peacock",
|
|
||||||
"xdebug.php-debug",
|
|
||||||
"neilbrayfield.php-docblocker",
|
|
||||||
"bmewburn.vscode-intelephense-client",
|
|
||||||
"valeryanm.vscode-phpsab",
|
|
||||||
"syler.sass-indented",
|
|
||||||
"sibiraj-s.vscode-scss-formatter",
|
|
||||||
"mrmlnc.vscode-scss",
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"vue.vscode-typescript-vue-plugin",
|
|
||||||
"wscats.vue",
|
|
||||||
"misterj.vue-volar-extention-pack"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
48
.vscode/launch.json
vendored
48
.vscode/launch.json
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
{
|
|
||||||
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
|
|
||||||
// Pointez pour afficher la description des attributs existants.
|
|
||||||
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Listen for Xdebug",
|
|
||||||
"type": "php",
|
|
||||||
"request": "launch",
|
|
||||||
"port": 9003
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Launch currently open script",
|
|
||||||
"type": "php",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${file}",
|
|
||||||
"cwd": "${fileDirname}",
|
|
||||||
"port": 0,
|
|
||||||
"runtimeArgs": [
|
|
||||||
"-dxdebug.start_with_request=yes"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"XDEBUG_MODE": "debug,develop",
|
|
||||||
"XDEBUG_CONFIG": "client_port=${port}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Launch Built-in web server",
|
|
||||||
"type": "php",
|
|
||||||
"request": "launch",
|
|
||||||
"runtimeArgs": [
|
|
||||||
"-dxdebug.mode=debug",
|
|
||||||
"-dxdebug.start_with_request=yes",
|
|
||||||
"-S",
|
|
||||||
"localhost:0"
|
|
||||||
],
|
|
||||||
"program": "",
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"port": 9003,
|
|
||||||
"serverReadyAction": {
|
|
||||||
"pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
|
|
||||||
"uriFormat": "http://localhost:%s",
|
|
||||||
"action": "openExternally"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
41
.vscode/settings.json
vendored
41
.vscode/settings.json
vendored
|
|
@ -1,26 +1,23 @@
|
||||||
{
|
{
|
||||||
"workbench.colorCustomizations": {
|
"workbench.colorCustomizations": {
|
||||||
"activityBar.activeBackground": "#e6f0f8",
|
"activityBar.activeBorder": "#621e41",
|
||||||
"activityBar.activeBorder": "#1a1d02",
|
"activityBar.activeBackground": "#3371a9",
|
||||||
"activityBar.background": "#e6f0f8",
|
"activityBar.background": "#3371a9",
|
||||||
"activityBar.foreground": "#15202b",
|
"activityBar.foreground": "#e7e7e7",
|
||||||
"activityBar.inactiveForeground": "#15202b99",
|
"activityBar.inactiveForeground": "#e7e7e799",
|
||||||
"activityBarBadge.background": "#dc82b4",
|
"activityBarBadge.background": "#621e41",
|
||||||
"activityBarBadge.foreground": "#15202b",
|
"activityBarBadge.foreground": "#e7e7e7",
|
||||||
"sash.hoverBorder": "#e6f0f8",
|
"commandCenter.border": "#e7e7e799",
|
||||||
"statusBar.background": "#bdd7ee",
|
"sash.hoverBorder": "#3371a9",
|
||||||
"statusBar.foreground": "#15202b",
|
"statusBar.background": "#275782",
|
||||||
"statusBarItem.hoverBackground": "#94bee4",
|
"statusBar.foreground": "#e7e7e7",
|
||||||
"statusBarItem.remoteBackground": "#bdd7ee",
|
"statusBarItem.hoverBackground": "#3371a9",
|
||||||
"statusBarItem.remoteForeground": "#15202b",
|
"statusBarItem.remoteBackground": "#275782",
|
||||||
"titleBar.activeBackground": "#bdd7ee",
|
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||||
"titleBar.activeForeground": "#15202b",
|
"titleBar.activeBackground": "#275782",
|
||||||
"titleBar.inactiveBackground": "#bdd7ee99",
|
"titleBar.activeForeground": "#e7e7e7",
|
||||||
"titleBar.inactiveForeground": "#15202b99",
|
"titleBar.inactiveBackground": "#27578299",
|
||||||
"commandCenter.border": "#15202b99"
|
"titleBar.inactiveForeground": "#e7e7e799"
|
||||||
},
|
},
|
||||||
"peacock.remoteColor": "#BDD7EE",
|
"peacock.remoteColor": "#275782",
|
||||||
"intelephense.environment.phpVersion": "8.3.0",
|
|
||||||
"intelephense.completion.fullyQualifyGlobalConstantsAndFunctions": true,
|
|
||||||
"intelephense.phpdoc.useFullyQualifiedNames": true
|
|
||||||
}
|
}
|
||||||
103
CHANGELOG.md
103
CHANGELOG.md
|
|
@ -1,12 +1,99 @@
|
||||||
# Changelog
|
### 25.04.2023 -> 1.4.0
|
||||||
|
- Add ticket types management
|
||||||
|
- Fix AutoClose Jobs
|
||||||
|
- Fix Email notifications on private messages
|
||||||
|
### 04.2023 -> 1.3.0
|
||||||
|
- Update Status Enum
|
||||||
|
- Update TicketStatusChanged event
|
||||||
|
- Update TicketStatus Changed messages
|
||||||
|
- Add DutyHours
|
||||||
|
- Add Informations
|
||||||
|
### 15.12.2022
|
||||||
|
- Add Sentry
|
||||||
|
### 12.10.2022 -> 1.2.3
|
||||||
|
- Hotfixed regular users could not create tickets anymore
|
||||||
|
|
||||||
All notable changes to `laravel-project` will be documented in this file.
|
### 01.07.2022 -> 1.2.2
|
||||||
|
- Fixed CSP directives
|
||||||
|
- Fixed multiples bugs
|
||||||
|
- Fixed View sur crédits
|
||||||
|
- Fixed file create
|
||||||
|
- Changed estimation can be 0.125 days
|
||||||
|
- Fixed files view
|
||||||
|
- Added ticket link on credit log
|
||||||
|
|
||||||
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
### 09.06.2022 -> 1.2.1
|
||||||
- This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
- CSP rules enforce
|
||||||
- Commits respect [Conventionnal commits](https://www.conventionalcommits.org/en/v1.0.0/) & use [Gitmoji](https://gitmoji.dev/).
|
- Bug fixes CSP issues
|
||||||
|
### 31.05.2022 -> 1.2.0
|
||||||
|
- Ajout de la fonctionnalitée d'estimations
|
||||||
|
|
||||||
## **[v1.0.0] - 01/01/2026**
|
### 26.05.2022 -> 1.1.0
|
||||||
|
- Correction de regression sur macro planning
|
||||||
|
- Correction de regression sur droits planning
|
||||||
|
- Ajout de fonctionnalité Label
|
||||||
|
- Ajout de fonctionalité assigner un ticket
|
||||||
|
- Rafactorisation du code ticket
|
||||||
|
- Refonte de tickets.index
|
||||||
|
- Ajout CSP report URI
|
||||||
|
- Remove external JS and CSS dependencies
|
||||||
|
- Use Vuejs runtime
|
||||||
|
- Add xss sanitizer
|
||||||
|
- Normalize Laravel mix + add cleaner
|
||||||
|
- Update npm deps
|
||||||
|
- Upgrade bootswatch theme
|
||||||
|
- ADD Secure HEADERS
|
||||||
|
|
||||||
### Added
|
### 23.05.2022 -> 1.0.9
|
||||||
- feat: 🎉 working project
|
- Correction d'erreurs multiples
|
||||||
|
- Fonctionnalité macro planning
|
||||||
|
- Mises à jour des dépendances
|
||||||
|
|
||||||
|
### 15.03.2022
|
||||||
|
- le presse-papier du planning n'est plus vidé après un collage
|
||||||
|
|
||||||
|
### 08.03.2022 -> 1.0.8
|
||||||
|
- passage à 8 slots dans le planning
|
||||||
|
|
||||||
|
### 03.01.2022 -> 1.0.7
|
||||||
|
- nouveau stockage pour le planning
|
||||||
|
|
||||||
|
### 01.12 -> 1.0.6
|
||||||
|
- Ajout d'un nouveau type de Ticket : Demande relative aux Newsletters
|
||||||
|
|
||||||
|
### 25.10 -> 1.0.5
|
||||||
|
- Ajout d'un nouveau type de Ticket : Demande relative aux BR, ceci afin de faire une demande
|
||||||
|
qui concernerait les bons de réduction
|
||||||
|
- Correction Plannings
|
||||||
|
- Correction Crud
|
||||||
|
- Correction de mails relatifs au ticket pour y inclure le bon type de ticket
|
||||||
|
|
||||||
|
|
||||||
|
### 30.09 -> 1.0.4
|
||||||
|
- Correction lors de l'apperçu des messages
|
||||||
|
- Correction lors de message sur affichage (auteur message)
|
||||||
|
- Modification de la couleur BG sur label statut du ticket
|
||||||
|
- Modification de l'url précédent sur ticket.show (garde en mémoire le filtre multi projet)
|
||||||
|
- Modification Tri par défaut sur ticket id DESC
|
||||||
|
|
||||||
|
### 29.09 -> 1.0.3
|
||||||
|
- Correction des avertissements avant cloture des tickets
|
||||||
|
- Ajout d'un bouton pour les utilisateurs afin de réouvrir un ticket clos
|
||||||
|
- Ajout de messages de confirmation sur les actions de tickets
|
||||||
|
|
||||||
|
### 29.09 -> 1.0.2
|
||||||
|
- Modification Un ticket qui est marqué comme traité ou en production depuis plus de 15 jours et sans activité sera automatiquement clos et archivé
|
||||||
|
- Modification Il n'est désomrais plus possible de répondre à un ticket archivé dans touts les cas
|
||||||
|
- Nettoyage
|
||||||
|
- Correction des noms qui ne s'affichaient pas correctement sur les tickets
|
||||||
|
|
||||||
|
### 01.08 -> 0.0.1
|
||||||
|
- Ajout d'un algo de charge
|
||||||
|
- Ajout changement de mot de passe
|
||||||
|
- Ajout messages http explicites
|
||||||
|
- Correction couleurs
|
||||||
|
|
||||||
|
#### 30.07
|
||||||
|
- Ajout changelog
|
||||||
|
- Ajout footer infos
|
||||||
|
- Correction couleurs
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM composer:2.8 AS vendor
|
FROM composer:2.9.2 AS vendor
|
||||||
|
|
||||||
ARG GITLAB_USER
|
ARG GITLAB_USER
|
||||||
ARG GITLAB_PASSWORD
|
ARG GITLAB_PASSWORD
|
||||||
|
|
@ -23,23 +23,18 @@ ARG GITLAB_PASSWORD
|
||||||
|
|
||||||
RUN mkdir -p /app/public/build
|
RUN mkdir -p /app/public/build
|
||||||
|
|
||||||
COPY package*.json postcss.config.js tailwind.config.js tsconfig.json vite.config.ts .eslintrc.json .stylelintrc.json /app/
|
COPY package*.json tsconfig.json vite.config.ts .eslintrc.json .stylelintrc.json /app/
|
||||||
COPY resources/ /app/resources/
|
COPY resources/ /app/resources/
|
||||||
COPY --from=vendor /app/vendor/ /app/vendor/
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN npm config set @ldc-web:registry https://lab.s-web.fr/api/v4/packages/npm/
|
|
||||||
RUN npm config set -- '//lab.s-web.fr/api/v4/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
|
|
||||||
RUN npm config set @uwil:registry https://lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/
|
RUN npm config set @uwil:registry https://lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/
|
||||||
RUN npm config set -- '//lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
|
RUN npm config set -- '//lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
|
||||||
RUN npm ci --no-audit --no-fund --loglevel=error && npm run prod
|
RUN npm ci --no-audit --no-fund --loglevel=error && npm run prod
|
||||||
|
|
||||||
|
|
||||||
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/uwil-production-8.5:${PHP_8_5_TAG}
|
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/uwil-production-8.5:${PHP_8_5_TAG}
|
||||||
|
|
||||||
ARG USER=www-data
|
ARG USER=www-data
|
||||||
|
|
||||||
COPY --chown=${USER}:${USER} . /app/srcs
|
COPY --chown=${USER}:${USER} . /app/srcs
|
||||||
COPY --chown=${USER}:${USER} ./resources/favicons/back/ /app/srcs/public/favicon-back/
|
|
||||||
COPY --from=vendor --chown=${USER}:${USER} /app/vendor/ /app/srcs/vendor/
|
COPY --from=vendor --chown=${USER}:${USER} /app/vendor/ /app/srcs/vendor/
|
||||||
COPY --from=frontend --chown=${USER}:${USER} /app/public/build/ /app/srcs/public/build/
|
COPY --from=frontend --chown=${USER}:${USER} /app/public/build/ /app/srcs/public/build/
|
||||||
|
|
||||||
|
|
|
||||||
34
IDEAS.md
Normal file
34
IDEAS.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
Algorithme charge de travail ticket
|
||||||
|
|
||||||
|
|
||||||
|
delais 7 jours 168h
|
||||||
|
travaillé 5 jours 37h
|
||||||
|
temps tickets 1 jours 7.4h
|
||||||
|
|
||||||
|
personnes 4 (TpP) 3.7h / semaine * prs
|
||||||
|
|
||||||
|
délais moyen ticket (Dmt) 0.75h
|
||||||
|
|
||||||
|
délais max attente pour 1 ticket (Dam) 7jours
|
||||||
|
|
||||||
|
niveaux de charge 4: - faible
|
||||||
|
- stable
|
||||||
|
- élevée
|
||||||
|
- saturation
|
||||||
|
|
||||||
|
états tickets(Et) : - nouveaux (non traités)
|
||||||
|
- en attente (charge pendante)
|
||||||
|
- traités
|
||||||
|
|
||||||
|
Algo : linéaire
|
||||||
|
|
||||||
|
TpP : 3.7h
|
||||||
|
P : 4
|
||||||
|
|
||||||
|
Et(nvx) * Dam
|
||||||
|
|
||||||
|
Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
|
||||||
|
|
||||||
|
|
||||||
|
Si nv Charge < 0 => saturation
|
||||||
|
|
||||||
36
README.md
36
README.md
|
|
@ -1,33 +1,17 @@
|
||||||
# Table of contents
|
# UWIL Support
|
||||||
|
|
||||||
- [Table of contents](#table-of-contents)
|
PROD : support.uwil.fr
|
||||||
- [Frameworks, Platforms and Libraries](#frameworks-platforms-and-libraries)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [Changelog](#changelog)
|
|
||||||
- [Copyright and License](#copyright-and-license)
|
|
||||||
|
|
||||||
# Frameworks, Platforms and Libraries
|
RE7 : support-web.re7-s-web.fr
|
||||||
|
|
||||||
<!-- https://github.com/Ileriayo/markdown-badges -->
|
## Gitlab Intégration
|
||||||
|
Le token de groupe pour l'intégration est valable 11 mois
|
||||||
|
|
||||||
[](https://sass-lang.com/)
|
Il faut le renouveler ici : https://lab.s-web.fr/groups/ldc-web/projets/-/settings/access_tokens et le nommer SW Intégration
|
||||||
[](https://tailwindcss.com/)
|
|
||||||
[](https://vite.dev/)
|
|
||||||
[](https://www.php.net/)
|
|
||||||
[](https://laravel.com/docs/master)
|
|
||||||
|
|
||||||
# Documentation
|
Role : Guest
|
||||||
|
Scope : api
|
||||||
|
|
||||||
```php
|
## TODO
|
||||||
composer i/npm i
|
|
||||||
```
|
|
||||||
|
|
||||||
# Changelog
|
- App\Models\Message:booted(): investigate why this make errors on prod,
|
||||||
|
|
||||||
Latest version v1.0.0.
|
|
||||||
|
|
||||||
See the [CHANGELOG.md](CHANGELOG.md) file for details.
|
|
||||||
|
|
||||||
# Copyright and License
|
|
||||||
|
|
||||||
Copyright (c) 2025 and beyond UWIL.
|
|
||||||
|
|
|
||||||
4633
_ide_helper.php
Normal file
4633
_ide_helper.php
Normal file
File diff suppressed because it is too large
Load diff
56
app/Casts/HtmlColor.php
Normal file
56
app/Casts/HtmlColor.php
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Casts;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom cast Html Color
|
||||||
|
* https://laravel.com/docs/8.x/eloquent-mutators#custom-casts
|
||||||
|
*/
|
||||||
|
class HtmlColor implements CastsAttributes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Cast the given value.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return string
|
||||||
|
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||||
|
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||||
|
*/
|
||||||
|
public function get($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
return "#{$this->zeropad(dechex($value), 6)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the given value for storage.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param array $attributes
|
||||||
|
* @return integer
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
|
||||||
|
*/
|
||||||
|
public function set($model, $key, $value, $attributes)
|
||||||
|
{
|
||||||
|
return \intval(hexdec(ltrim($value, '#')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad with zeros a numeric string
|
||||||
|
*
|
||||||
|
* @param string $num
|
||||||
|
* @param integer $lim
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function zeropad(string $num, int $lim): string
|
||||||
|
{
|
||||||
|
return (strlen($num) >= $lim) ? $num : $this->zeropad("0" . $num, $lim);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Console/Commands/AutoCloseOldTickets.php
Normal file
38
app/Console/Commands/AutoCloseOldTickets.php
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\AutoCloseOldTickets as JobsAutoCloseOldTickets;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class AutoCloseOldTickets extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'tickets:auto-close-old';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Run the AutoCloseOldTickets job.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->info('Running the Job, this could take some time.');
|
||||||
|
$job = new JobsAutoCloseOldTickets();
|
||||||
|
$job->handle();
|
||||||
|
$this->info('Job completed');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
168
app/Console/Commands/CreateUser.php
Normal file
168
app/Console/Commands/CreateUser.php
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\Users\RoleEnum;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class CreateUser extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'user:create';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Create a user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the new user.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The email of the new user.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password of the new user.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The role of the new user.
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->name = null;
|
||||||
|
$this->addName();
|
||||||
|
|
||||||
|
$this->email = null;
|
||||||
|
$this->addEmail();
|
||||||
|
|
||||||
|
$this->password = null;
|
||||||
|
$this->addPassword();
|
||||||
|
|
||||||
|
$this->role = null;
|
||||||
|
$this->addRole();
|
||||||
|
|
||||||
|
$user = new User([
|
||||||
|
'name' => $this->name,
|
||||||
|
'email' => $this->email,
|
||||||
|
'password' => $this->password,
|
||||||
|
'role' => $this->role
|
||||||
|
]);
|
||||||
|
$user->saveOrFail();
|
||||||
|
|
||||||
|
$this->info('User created.');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a firstname to the user.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addName(): void
|
||||||
|
{
|
||||||
|
while (is_null($this->name)) {
|
||||||
|
try {
|
||||||
|
$tmp = $this->ask('Type the wanted user name', 'Admin');
|
||||||
|
$validator = Validator::make(['name' => $tmp], [
|
||||||
|
'name' => 'required|min:3|max:255'
|
||||||
|
]);
|
||||||
|
$validator->validate();
|
||||||
|
$this->name = $tmp;
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$this->error(sprintf(
|
||||||
|
'Please provide a valid name %s',
|
||||||
|
\implode(',', collect($e->errors())->flatten()->all())
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
} //end try
|
||||||
|
}; //end while
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an email to the user.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addEmail(): void
|
||||||
|
{
|
||||||
|
while (is_null($this->email)) {
|
||||||
|
try {
|
||||||
|
$tmp = $this->ask('Type the wanted user email', 'digital@ldc.fr');
|
||||||
|
$validator = Validator::make(['email' => $tmp], [
|
||||||
|
'email' => 'required|unique:users,email|email:rfc,dns'
|
||||||
|
]);
|
||||||
|
$validator->validate();
|
||||||
|
$this->email = $tmp;
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$this->error(sprintf(
|
||||||
|
'Please provide a valid email %s',
|
||||||
|
\implode(',', collect($e->errors())->flatten()->all())
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
} //end try
|
||||||
|
}; //end while
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a password to the user.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addPassword(): void
|
||||||
|
{
|
||||||
|
while (
|
||||||
|
($this->password = $this->secret(
|
||||||
|
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password'
|
||||||
|
)) !== $this->secret('Confirm the password')
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}; //end while
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a password to the user.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function addRole(): void
|
||||||
|
{
|
||||||
|
while (is_null($this->role)) {
|
||||||
|
$tmp = $this->confirm('Is the user an admin?');
|
||||||
|
$validator = Validator::make(['role' => $tmp], [
|
||||||
|
'role' => 'required|boolean'
|
||||||
|
]);
|
||||||
|
$validator->validate();
|
||||||
|
$this->role = $tmp ? RoleEnum::admin : RoleEnum::client;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Console/Commands/ExportTicketsState.php
Normal file
38
app/Console/Commands/ExportTicketsState.php
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\ExportTicketsState as JobsExportTicketsState;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class ExportTicketsState extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'tickets:export-state';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Export the state of tickets to a CSV file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->info('Running the Job, this could take some time.');
|
||||||
|
$job = new JobsExportTicketsState();
|
||||||
|
$job->handle();
|
||||||
|
$this->info('Job completed');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Date;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\Validation\Rules\Password;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
use Uwil\LaravelBackend\Support\UserRoleRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Artisan command to create a support admin user.
|
|
||||||
*/
|
|
||||||
class MakeUser extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
protected $signature = 'support:make:user';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
protected $description = 'Create a new Support user.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$this->components->info('Creating a new <comment>Support user</comment>...');
|
|
||||||
|
|
||||||
$firstName = $this->askValid(
|
|
||||||
'first_name',
|
|
||||||
'First name',
|
|
||||||
'Admin',
|
|
||||||
['required', 'min:3', 'max:255'],
|
|
||||||
);
|
|
||||||
$lastName = $this->askValid(
|
|
||||||
'last_name',
|
|
||||||
'Last name',
|
|
||||||
'Saïtama',
|
|
||||||
['required', 'min:3', 'max:255'],
|
|
||||||
);
|
|
||||||
$email = $this->askValid(
|
|
||||||
'email',
|
|
||||||
'Email',
|
|
||||||
'equipe.web@ldc.fr',
|
|
||||||
['required', 'unique:users,email', 'email:rfc,strict,dns,spoof,filter'],
|
|
||||||
);
|
|
||||||
$role = $this->askRole();
|
|
||||||
$password = $this->askPassword();
|
|
||||||
|
|
||||||
$user = new User([
|
|
||||||
'first_name' => $firstName,
|
|
||||||
'last_name' => $lastName,
|
|
||||||
'email' => $email,
|
|
||||||
'role' => $role->value,
|
|
||||||
'published' => true,
|
|
||||||
'published_at' => Date::now(),
|
|
||||||
]);
|
|
||||||
$user->saveOrFail();
|
|
||||||
|
|
||||||
$user->userPassword()->create([
|
|
||||||
'password' => bcrypt($password),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->components->success("User <comment>{$firstName} {$lastName}</comment> created successfully.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask for a value and validate it, re-prompting on failure.
|
|
||||||
*
|
|
||||||
* @param array<int, string> $rules
|
|
||||||
*/
|
|
||||||
private function askValid(string $field, string $label, string $default, array $rules): string
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$value = $this->ask(sprintf('Type the wanted user %s', $label), $default);
|
|
||||||
$validator = Validator::make([$field => $value], [$field => $rules]);
|
|
||||||
$validator->validate();
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
} catch (ValidationException $e) {
|
|
||||||
$this->components->error(
|
|
||||||
collect($e->errors())->flatten()->first()
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->askValid($field, $label, $default, $rules);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask for a role and return the corresponding RoleEnum.
|
|
||||||
*
|
|
||||||
* @return object{name: string, value: int|string, label: string}
|
|
||||||
*/
|
|
||||||
private function askRole(): object
|
|
||||||
{
|
|
||||||
$roles = UserRoleRegistry::all();
|
|
||||||
$defaultRole = UserRoleRegistry::get('client');
|
|
||||||
|
|
||||||
$options = collect($roles)->mapWithKeys(
|
|
||||||
fn (object $role) => [Str::ucFirst($role->label) => $role]
|
|
||||||
);
|
|
||||||
|
|
||||||
$choice = $this->choice(
|
|
||||||
'Role',
|
|
||||||
$options->keys()->all(),
|
|
||||||
Str::ucFirst($defaultRole->label),
|
|
||||||
);
|
|
||||||
$choiceKey = is_string($choice) ? $choice : (string) $choice[0];
|
|
||||||
|
|
||||||
return $options->get($choiceKey) ?? $defaultRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask for a password and confirmation password.
|
|
||||||
*/
|
|
||||||
private function askPassword(): string
|
|
||||||
{
|
|
||||||
$password = $this->secret('Type the wanted user Password');
|
|
||||||
$passwordConfirmation = $this->secret('Confirm the password');
|
|
||||||
|
|
||||||
try {
|
|
||||||
Validator::make(
|
|
||||||
[
|
|
||||||
'password' => $password,
|
|
||||||
'password_confirmation' => $passwordConfirmation,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'password' => [
|
|
||||||
'required',
|
|
||||||
'confirmed',
|
|
||||||
'max:255',
|
|
||||||
Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised(),
|
|
||||||
],
|
|
||||||
]
|
|
||||||
)->validate();
|
|
||||||
|
|
||||||
return $password;
|
|
||||||
} catch (ValidationException $e) {
|
|
||||||
$this->components->error(collect($e->errors())->flatten()->first());
|
|
||||||
|
|
||||||
return $this->askPassword();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
app/Console/Commands/TestMail.php
Normal file
36
app/Console/Commands/TestMail.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class TestMail extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'uwil:mail-test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Send test email to digital@ldc.fr';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) {
|
||||||
|
$msg->to('digital@ldc.fr')->subject('Test Email');
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
334
app/Console/Commands/UpdateGitlabDatas.php
Normal file
334
app/Console/Commands/UpdateGitlabDatas.php
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\Tickets\TicketStatusEnum;
|
||||||
|
use App\Models\GitlabIssues;
|
||||||
|
use App\Models\GitlabLabels;
|
||||||
|
use App\Models\GitlabMilestones;
|
||||||
|
use App\Models\GitlabProjects;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use GrahamCampbell\GitLab\Facades\GitLab;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class UpdateGitlabDatas extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:update-gitlab-datas {--is-cron}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Fetch some Gitlab datas';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$isCron = $this->option('is-cron');
|
||||||
|
|
||||||
|
$groupId = 23;
|
||||||
|
|
||||||
|
$this->updateProjectsByGroup($groupId);
|
||||||
|
|
||||||
|
$this->updateLabelsByGroup($groupId);
|
||||||
|
|
||||||
|
$projects = GitlabProjects::all();
|
||||||
|
|
||||||
|
foreach ($projects as $project) {
|
||||||
|
$this->updateMilestonesByProject($project);
|
||||||
|
$this->updateIssuesByProject($project, $isCron);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->syncTicketStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateProjectsByGroup function
|
||||||
|
*
|
||||||
|
* @param integer $groupId
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateProjectsByGroup(int $groupId): void
|
||||||
|
{
|
||||||
|
$allProjects = [];
|
||||||
|
$page = 1;
|
||||||
|
do {
|
||||||
|
$projects = GitLab::groups()->projects($groupId, [
|
||||||
|
'order_by' => 'created_at',
|
||||||
|
'sort' => 'asc',
|
||||||
|
'per_page' => 100,
|
||||||
|
'page' => $page,
|
||||||
|
'include_subgroups' => true
|
||||||
|
]);
|
||||||
|
$allProjects = array_merge($allProjects, $projects);
|
||||||
|
$page++;
|
||||||
|
} while (!empty($projects));
|
||||||
|
|
||||||
|
$this->info(sprintf(
|
||||||
|
"Fetched %d projects in group %d.",
|
||||||
|
count($allProjects),
|
||||||
|
$groupId
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($allProjects as $project) {
|
||||||
|
GitlabProjects::upsert(
|
||||||
|
[
|
||||||
|
'name' => $project['name'],
|
||||||
|
'url' => $project['web_url'],
|
||||||
|
'is_archived' => $project['archived'],
|
||||||
|
'gitlab_id' => $project['id']
|
||||||
|
],
|
||||||
|
['gitlab_id']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Gitlab Projects upsert complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateLabelsByGroup function
|
||||||
|
*
|
||||||
|
* @param integer $groupId
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateLabelsByGroup(int $groupId): void
|
||||||
|
{
|
||||||
|
$allLabels = [];
|
||||||
|
$page = 1;
|
||||||
|
do {
|
||||||
|
$labels = GitLab::groups()->labels($groupId, [
|
||||||
|
'per_page' => 100,
|
||||||
|
'page' => $page,
|
||||||
|
]);
|
||||||
|
$allLabels = array_merge($allLabels, $labels);
|
||||||
|
$page++;
|
||||||
|
} while (!empty($labels));
|
||||||
|
|
||||||
|
$this->info(sprintf(
|
||||||
|
"Fetched %d labels in group %d.",
|
||||||
|
count($allLabels),
|
||||||
|
$groupId
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($allLabels as $project) {
|
||||||
|
GitlabLabels::upsert(
|
||||||
|
[
|
||||||
|
'name' => $project['name'],
|
||||||
|
'gitlab_id' => $project['id']
|
||||||
|
],
|
||||||
|
['gitlab_id']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Gitlab labels upsert complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateIssuesByProject function
|
||||||
|
*
|
||||||
|
* @param GitlabProjects $project
|
||||||
|
* @param boolean $isCron
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateIssuesByProject(GitlabProjects $project, bool $isCron = false): void
|
||||||
|
{
|
||||||
|
$projectId = $project->gitlab_id;
|
||||||
|
$allIssues = [];
|
||||||
|
$page = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$params = [
|
||||||
|
'order_by' => 'created_at',
|
||||||
|
'sort' => 'asc',
|
||||||
|
'per_page' => 100,
|
||||||
|
'page' => $page,
|
||||||
|
'state' => 'all',
|
||||||
|
];
|
||||||
|
if ($isCron) {
|
||||||
|
$params = array_merge($params, ['updated_after' => now()->subDay()]);
|
||||||
|
}
|
||||||
|
$issues = GitLab::projects()->issues($projectId, $params);
|
||||||
|
$allIssues = array_merge($allIssues, $issues);
|
||||||
|
$page++;
|
||||||
|
} while (!empty($issues));
|
||||||
|
|
||||||
|
$this->info(sprintf(
|
||||||
|
"Fetched %d issues in project %d.",
|
||||||
|
count($allIssues),
|
||||||
|
$projectId
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($allIssues as $issue) {
|
||||||
|
GitlabIssues::upsert(
|
||||||
|
[
|
||||||
|
'title' => $issue['title'],
|
||||||
|
'url' => $issue['web_url'],
|
||||||
|
'estimate_time' => $issue['time_stats']['time_estimate'] ?? 0,
|
||||||
|
'spent_time' => $issue['time_stats']['total_time_spent'] ?? 0,
|
||||||
|
'is_closed' => $issue['state'] === 'closed',
|
||||||
|
'gitlab_id' => $issue['id'],
|
||||||
|
'gitlab_iid' => $issue['iid'],
|
||||||
|
'milestone_iid' => $issue['milestone']['iid'] ?? null,
|
||||||
|
'gitlab_project_id' => $projectId,
|
||||||
|
'g_created_at' => Carbon::parse($issue['created_at'])->format('Y-m-d'),
|
||||||
|
'g_updated_at' => Carbon::parse($issue['updated_at'])->format('Y-m-d'),
|
||||||
|
'g_closed_at' => !is_null($issue['closed_at'])
|
||||||
|
? Carbon::parse($issue['closed_at'])->format('Y-m-d')
|
||||||
|
: null,
|
||||||
|
'g_due_date' => !is_null($issue['due_date'])
|
||||||
|
? Carbon::parse($issue['due_date'])->format('Y-m-d')
|
||||||
|
: null
|
||||||
|
],
|
||||||
|
['gitlab_id']
|
||||||
|
);
|
||||||
|
|
||||||
|
$issueModel = GitlabIssues::where('gitlab_id', $issue['id'])->firstOrFail();
|
||||||
|
$labelIds = [];
|
||||||
|
|
||||||
|
foreach ($issue['labels'] as $labelName) {
|
||||||
|
$label = Cache::remember("gitlab_label_{$labelName}", 300, function () use ($labelName) {
|
||||||
|
return GitlabLabels::where('name', $labelName)->first();
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($label) {
|
||||||
|
$labelIds[] = $label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$issueModel->gitlabLabels()->sync($labelIds);
|
||||||
|
}//end foreach
|
||||||
|
|
||||||
|
$this->info("Gitlab issues upsert complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateMilestonesByProject function
|
||||||
|
*
|
||||||
|
* @param GitlabProjects $project
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateMilestonesByProject(GitlabProjects $project): void
|
||||||
|
{
|
||||||
|
$projectId = $project->gitlab_id;
|
||||||
|
$allMilestones = [];
|
||||||
|
$page = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$milestones = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$milestones = GitLab::milestones()->all($projectId, [
|
||||||
|
'per_page' => 100,
|
||||||
|
'page' => $page
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($e->getCode() === 404) {
|
||||||
|
if ($project->delete()) {
|
||||||
|
Log::warning(sprintf(
|
||||||
|
"Project not found (%s), it has been deleted.",
|
||||||
|
$projectId
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
Log::warning(sprintf(
|
||||||
|
"Project not found (%s).",
|
||||||
|
$projectId
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log::error(sprintf(
|
||||||
|
'API Error for project %s, Message: %d',
|
||||||
|
$projectId,
|
||||||
|
$e->getMessage()
|
||||||
|
));
|
||||||
|
report($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allMilestones = array_merge($allMilestones, $milestones);
|
||||||
|
$page++;
|
||||||
|
} while (!empty($milestones));
|
||||||
|
|
||||||
|
$this->info(sprintf(
|
||||||
|
"Fetched %d milestones in project %d.",
|
||||||
|
count($allMilestones),
|
||||||
|
$projectId
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($allMilestones as $milestone) {
|
||||||
|
GitlabMilestones::upsert(
|
||||||
|
[
|
||||||
|
'title' => $milestone['title'],
|
||||||
|
'url' => $milestone['web_url'],
|
||||||
|
'is_closed' => $milestone['state'] === 'closed',
|
||||||
|
'gitlab_id' => $milestone['id'],
|
||||||
|
'gitlab_iid' => $milestone['iid'],
|
||||||
|
'gitlab_project_id' => $projectId,
|
||||||
|
'g_created_at' => Carbon::parse($milestone['created_at'])->format('Y-m-d'),
|
||||||
|
'g_updated_at' => Carbon::parse($milestone['updated_at'])->format('Y-m-d'),
|
||||||
|
'g_start_date' => !is_null($milestone['start_date'])
|
||||||
|
? Carbon::parse($milestone['start_date'])->format('Y-m-d')
|
||||||
|
: null,
|
||||||
|
'g_due_date' => !is_null($milestone['due_date'])
|
||||||
|
? Carbon::parse($milestone['due_date'])->format('Y-m-d')
|
||||||
|
: null
|
||||||
|
],
|
||||||
|
['gitlab_id']
|
||||||
|
);
|
||||||
|
}//end foreach
|
||||||
|
|
||||||
|
$this->info("Gitlab milestones upsert complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes ticket statuses based on GitLab issue labels.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function syncTicketStatus(): void
|
||||||
|
{
|
||||||
|
$tickets = Ticket::whereNotNull('gitlab_issue')->whereNot('status', TicketStatusEnum::archived)->get();
|
||||||
|
|
||||||
|
foreach ($tickets as $ticket) {
|
||||||
|
$gitlabIssue = GitlabIssues::where('url', $ticket->gitlab_issue)->with('gitlabLabels')->first();
|
||||||
|
|
||||||
|
$gitlabIssueLabels = $gitlabIssue ? $gitlabIssue->gitlabLabels->pluck('name') : [];
|
||||||
|
|
||||||
|
if ($gitlabIssueLabels) {
|
||||||
|
match (true) {
|
||||||
|
$gitlabIssueLabels->contains('g-wip') => $this->updateTicketStatus($ticket, TicketStatusEnum::inDev),
|
||||||
|
$gitlabIssueLabels->contains('g-production') => $this->updateTicketStatus($ticket, TicketStatusEnum::production),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ticket status only if it has changed.
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket The ticket to update.
|
||||||
|
* @param TicketStatusEnum $status The new status to apply.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateTicketStatus(Ticket $ticket, TicketStatusEnum $status): void
|
||||||
|
{
|
||||||
|
if ($ticket->status !== $status && $ticket->status !== TicketStatusEnum::waiting) {
|
||||||
|
$ticket->status = $status;
|
||||||
|
$ticket->saveOrFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Console/Commands/WarnBeforeAutoCloseTicket.php
Normal file
38
app/Console/Commands/WarnBeforeAutoCloseTicket.php
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\WarnBeforeAutoCloseTicket as JobsWarnBeforeAutoCloseTicket;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class WarnBeforeAutoCloseTicket extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'tickets:warn-before-auto-close';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Run the WarnBeforeAutoCloseTicket job.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->info('Running the Job, this could take some time.');
|
||||||
|
$job = new JobsWarnBeforeAutoCloseTicket();
|
||||||
|
$job->handle();
|
||||||
|
$this->info('Job completed');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Console/Kernel.php
Normal file
44
app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use App\Jobs\AutoCloseOldTickets;
|
||||||
|
use App\Jobs\ExportTicketsState;
|
||||||
|
use App\Jobs\WarnBeforeAutoCloseTicket;
|
||||||
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Artisan commands provided by your application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $commands = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the application's command schedule.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function schedule(Schedule $schedule)
|
||||||
|
{
|
||||||
|
$schedule->job(ExportTicketsState::class)->weekly()->fridays()->at('14:00');
|
||||||
|
$schedule->job(AutoCloseOldTickets::class)->dailyAt('08:00');
|
||||||
|
$schedule->job(WarnBeforeAutoCloseTicket::class)->dailyAt('08:05');
|
||||||
|
$schedule->command('app:update-gitlab-datas --is-cron')->hourly()->between('7:00', '19:00');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the commands for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function commands()
|
||||||
|
{
|
||||||
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
require base_path('routes/console.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/Dto/BrevoWebHookDto.php
Normal file
13
app/Dto/BrevoWebHookDto.php
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto;
|
||||||
|
|
||||||
|
final readonly class BrevoWebHookDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public readonly string $plainTextContent,
|
||||||
|
public readonly ?string $brand
|
||||||
|
) {}
|
||||||
|
}
|
||||||
21
app/Enums/Credit/AccountTypeEnum.php
Normal file
21
app/Enums/Credit/AccountTypeEnum.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Credit;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum AccountTypeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case development = 0;
|
||||||
|
case seo = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::development->name => "Jours de développement",
|
||||||
|
self::seo->name => "Jours de seo",
|
||||||
|
];
|
||||||
|
}
|
||||||
21
app/Enums/Credit/BalanceOperationEnum.php
Normal file
21
app/Enums/Credit/BalanceOperationEnum.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Credit;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum BalanceOperationEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case credit = 0;
|
||||||
|
case debit = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::credit->name => "Créditer un compte",
|
||||||
|
self::debit->name => "Débiter un compte",
|
||||||
|
];
|
||||||
|
}
|
||||||
23
app/Enums/Credit/EstimationStatusEnum.php
Normal file
23
app/Enums/Credit/EstimationStatusEnum.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Credit;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum EstimationStatusEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case pending = 0;
|
||||||
|
case accepted = 1;
|
||||||
|
case refused = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::pending->name => "En attente",
|
||||||
|
self::accepted->name => "Acceptée",
|
||||||
|
self::refused->name => "Refusée",
|
||||||
|
];
|
||||||
|
}
|
||||||
49
app/Enums/Credit/EstimationTimeEnum.php
Normal file
49
app/Enums/Credit/EstimationTimeEnum.php
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Credit;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum EstimationTimeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case halfHour = 0.0625;
|
||||||
|
case hour = 0.125;
|
||||||
|
case hourAndHalf = 0.1875;
|
||||||
|
case twoHours = 0.25;
|
||||||
|
case twoHoursAndHalf = 0.3125;
|
||||||
|
case threeHours = 0.375;
|
||||||
|
case threeHoursAndHalf = 0.4375;
|
||||||
|
case fourHours = 0.5;
|
||||||
|
case fourHoursAndHalf = 0.5625;
|
||||||
|
case fiveHours = 0.625;
|
||||||
|
case fiveHoursAndHalf = 0.6875;
|
||||||
|
case sixHours = 0.75;
|
||||||
|
case sixHoursAndHalf = 0.8125;
|
||||||
|
case sevenHours = 0.875;
|
||||||
|
case sevenHoursAndHalf = 0.9375;
|
||||||
|
case eightHours = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::halfHour->name => "Une demi-heure",
|
||||||
|
self::hour->name => "Une heure",
|
||||||
|
self::hourAndHalf->name => "Une heure et demie",
|
||||||
|
self::twoHours->name => "Deux heures",
|
||||||
|
self::twoHoursAndHalf->name => "Deux heures et demie",
|
||||||
|
self::threeHours->name => "Trois heures",
|
||||||
|
self::twoHoursAndHalf->name => "Trois heures et demie",
|
||||||
|
self::fourHours->name => "Quatre heures",
|
||||||
|
self::fourHoursAndHalf->name => "Quatre heures et demie",
|
||||||
|
self::fiveHours->name => "Cinq heures",
|
||||||
|
self::fiveHoursAndHalf->name => "Cinq heures et demie",
|
||||||
|
self::sixHours->name => "Six heures",
|
||||||
|
self::sixHoursAndHalf->name => "Six heures et demie",
|
||||||
|
self::sevenHours->name => "Sept heures",
|
||||||
|
self::sevenHoursAndHalf->name => "Sept heures et demi",
|
||||||
|
self::eightHours->name => "Huit heures",
|
||||||
|
];
|
||||||
|
}
|
||||||
53
app/Enums/Informations/InfoColorsEnum.php
Normal file
53
app/Enums/Informations/InfoColorsEnum.php
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Informations;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum InfoColorsEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case primary = 0;
|
||||||
|
case secondary = 1;
|
||||||
|
case success = 2;
|
||||||
|
case danger = 3;
|
||||||
|
case warning = 4;
|
||||||
|
case info = 5;
|
||||||
|
case light = 6;
|
||||||
|
case dark = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const CLASSES = [
|
||||||
|
self::primary->name => "primary",
|
||||||
|
self::secondary->name => "secondary",
|
||||||
|
self::success->name => "success",
|
||||||
|
self::danger->name => "danger",
|
||||||
|
self::warning->name => "warning",
|
||||||
|
self::info->name => "info",
|
||||||
|
self::light->name => "light",
|
||||||
|
self::dark->name => "dark",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Class
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function class(): string
|
||||||
|
{
|
||||||
|
return self::CLASSES[$this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enum classes
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function toClasses(): array
|
||||||
|
{
|
||||||
|
return self::CLASSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Enums/Invoices/InvoiceStatusEnum.php
Normal file
29
app/Enums/Invoices/InvoiceStatusEnum.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Invoices;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum InvoiceStatusEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case opened = 1;
|
||||||
|
case refused = 2;
|
||||||
|
case canceled = 3;
|
||||||
|
case archived = 4;
|
||||||
|
case awaiting_signature = 5;
|
||||||
|
case awaiting_invoicing = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition.
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::opened->name => "Ouvert (nouveaux ou en cours)",
|
||||||
|
self::refused->name => "Refusé (par le client ou aucune réponse de sa part)",
|
||||||
|
self::canceled->name => "Annulé (supprimé)",
|
||||||
|
self::archived->name => "Archivé (validé et envoyé à la comptabilité)",
|
||||||
|
self::awaiting_signature->name => "En attente de signature",
|
||||||
|
self::awaiting_invoicing->name => "En attente de facturation",
|
||||||
|
];
|
||||||
|
}
|
||||||
57
app/Enums/Invoices/TaskTypeEnum.php
Normal file
57
app/Enums/Invoices/TaskTypeEnum.php
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Invoices;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum TaskTypeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case web = 1;
|
||||||
|
case seo = 2;
|
||||||
|
case package = 3;
|
||||||
|
case arbitrary = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::web->name => "Web",
|
||||||
|
self::seo->name => "SEO",
|
||||||
|
self::package->name => "Forfait",
|
||||||
|
self::arbitrary->name => "Libre",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom added prices definition
|
||||||
|
*/
|
||||||
|
private const PRICES = [
|
||||||
|
self::web->name => 600,
|
||||||
|
self::seo->name => 690,
|
||||||
|
// * Linked to packages table
|
||||||
|
self::package->name => 0,
|
||||||
|
// * Arbitrary number
|
||||||
|
self::arbitrary->name => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Price
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function price(): string
|
||||||
|
{
|
||||||
|
return self::PRICES[$this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enum prices
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function toPrices(): array
|
||||||
|
{
|
||||||
|
return self::PRICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Enums/ItemsPerPaginationEnum.php
Normal file
16
app/Enums/ItemsPerPaginationEnum.php
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum ItemsPerPaginationEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case five = 5;
|
||||||
|
case twelve = 12;
|
||||||
|
case twentytwo = 25;
|
||||||
|
case fifty = 50;
|
||||||
|
case onehundred = 100;
|
||||||
|
}
|
||||||
21
app/Enums/MessageTypeEnum.php
Normal file
21
app/Enums/MessageTypeEnum.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum MessageTypeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case private = 1;
|
||||||
|
case public = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::private->name => "Privé",
|
||||||
|
self::public->name => "Public",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Enums;
|
|
||||||
|
|
||||||
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the available information types.
|
|
||||||
*/
|
|
||||||
enum NoticeTypeEnum: int
|
|
||||||
{
|
|
||||||
use BaseEnum;
|
|
||||||
|
|
||||||
case primary = 1;
|
|
||||||
case secondary = 2;
|
|
||||||
case success = 3;
|
|
||||||
case danger = 4;
|
|
||||||
case warning = 5;
|
|
||||||
case info = 6;
|
|
||||||
case light = 7;
|
|
||||||
case dark = 8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optionnal labels definition
|
|
||||||
*/
|
|
||||||
private const CLASSES = [
|
|
||||||
self::primary->name => 'bg-primary text-primary-foreground',
|
|
||||||
self::secondary->name => 'bg-secondary text-secondary-foreground',
|
|
||||||
self::success->name => 'bg-successive text-successive-foreground',
|
|
||||||
self::danger->name => 'bg-destructive text-destructive-foreground',
|
|
||||||
self::warning->name => 'bg-preventive text-preventive-foreground',
|
|
||||||
self::info->name => 'bg-informative text-informative-foreground',
|
|
||||||
self::light->name => 'bg-muted text-muted-foreground',
|
|
||||||
self::dark->name => 'bg-foreground text-background',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Class.
|
|
||||||
*/
|
|
||||||
public function class(): string
|
|
||||||
{
|
|
||||||
return self::CLASSES[$this->name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get enum classes.
|
|
||||||
*
|
|
||||||
* @return array<string>
|
|
||||||
*/
|
|
||||||
public static function toClasses(): array
|
|
||||||
{
|
|
||||||
return self::CLASSES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
app/Enums/Tickets/TicketFileTypeEnum.php
Normal file
70
app/Enums/Tickets/TicketFileTypeEnum.php
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Tickets;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum TicketFileTypeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case png = '.png';
|
||||||
|
case jpeg = '.jpeg';
|
||||||
|
case jpg = '.jpg';
|
||||||
|
case pdf = '.pdf';
|
||||||
|
case pptx = '.pptx';
|
||||||
|
case docx = '.docx';
|
||||||
|
case xlsx = '.xlsx';
|
||||||
|
case csv = '.csv';
|
||||||
|
case svg = '.svg';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::png->name => "Fichier png",
|
||||||
|
self::jpeg->name => "Fichier jpeg",
|
||||||
|
self::jpg->name => "Fichier jpg",
|
||||||
|
self::pdf->name => "Fichier pdf",
|
||||||
|
self::pptx->name => "Fichier pptx",
|
||||||
|
self::docx->name => "Fichier docx",
|
||||||
|
self::xlsx->name => "Fichier xlsx",
|
||||||
|
self::csv->name => "Fichier csv",
|
||||||
|
self::svg->name => "Fichier svg",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom added mimes type definition
|
||||||
|
*/
|
||||||
|
private const MIMETYPE = [
|
||||||
|
self::png->name => 'image/png',
|
||||||
|
self::jpeg->name => 'image/jpeg',
|
||||||
|
self::jpg->name => 'image/jpeg',
|
||||||
|
self::pdf->name => 'application/pdf',
|
||||||
|
self::pptx->name => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||||
|
self::docx->name => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
self::xlsx->name => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
self::csv->name => 'text/csv',
|
||||||
|
self::svg->name => 'image/svg+xml',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Class
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function mimetypes(): string
|
||||||
|
{
|
||||||
|
return self::MIMETYPE[$this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enum classes
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function toMimetypes(): array
|
||||||
|
{
|
||||||
|
return self::MIMETYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
app/Enums/Tickets/TicketStatusEnum.php
Normal file
73
app/Enums/Tickets/TicketStatusEnum.php
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Tickets;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum TicketStatusEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case new = 1;
|
||||||
|
case estimateTodo = 2;
|
||||||
|
case takenInAccount = 3;
|
||||||
|
case waiting = 4;
|
||||||
|
case waitingValidation = 5;
|
||||||
|
case recette = 6;
|
||||||
|
case recetteValidated = 7;
|
||||||
|
case production = 8;
|
||||||
|
case archived = 9;
|
||||||
|
case inDev = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::new->name => "Nouveau",
|
||||||
|
self::estimateTodo->name => "Étude chef(s) de projet",
|
||||||
|
self::takenInAccount->name => "À planifier",
|
||||||
|
self::waiting->name => "En attente de réponse",
|
||||||
|
self::waitingValidation->name => "En attente de validation",
|
||||||
|
self::recette->name => "En recettage",
|
||||||
|
self::recetteValidated->name => "Recettage validé",
|
||||||
|
self::production->name => "En production",
|
||||||
|
self::archived->name => "Archivé",
|
||||||
|
self::inDev->name => "En cours de développement",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom added classes definition
|
||||||
|
*/
|
||||||
|
private const CLASSES = [
|
||||||
|
self::new->name => 'text-danger',
|
||||||
|
self::estimateTodo->name => 'text-info',
|
||||||
|
self::takenInAccount->name => 'text-info',
|
||||||
|
self::waiting->name => 'text-white bg-danger',
|
||||||
|
self::waitingValidation->name => 'text-warning bg-danger',
|
||||||
|
self::recette->name => 'text-primary',
|
||||||
|
self::recetteValidated->name => 'text-success',
|
||||||
|
self::production->name => 'text-success',
|
||||||
|
self::archived->name => 'text-secondary',
|
||||||
|
self::inDev->name => 'text-discovery',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Class
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function class(): string
|
||||||
|
{
|
||||||
|
return self::CLASSES[$this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enum classes
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function toClasses(): array
|
||||||
|
{
|
||||||
|
return self::CLASSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
app/Enums/Tickets/TicketTypeEnum.php
Normal file
67
app/Enums/Tickets/TicketTypeEnum.php
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Tickets;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum TicketTypeEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case bug = 1;
|
||||||
|
case evolution = 2;
|
||||||
|
case br = 3;
|
||||||
|
case newsletter = 4;
|
||||||
|
case qrcode = 5;
|
||||||
|
case advice = 6;
|
||||||
|
case complaints = 7;
|
||||||
|
case mailing = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::bug->name => "Bug",
|
||||||
|
self::evolution->name => "Évolution",
|
||||||
|
self::br->name => "Demande BR",
|
||||||
|
self::newsletter->name => "Demande newsletter",
|
||||||
|
self::qrcode->name => "Demande de QR Code",
|
||||||
|
self::advice->name => "Conseil - Accompagnement",
|
||||||
|
self::complaints->name => "Réclamations consommateurs",
|
||||||
|
self::mailing->name => "Demande mail en masse",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom added classes definition
|
||||||
|
*/
|
||||||
|
private const CLASSES = [
|
||||||
|
self::bug->name => "bg-danger",
|
||||||
|
self::evolution->name => "bg-success",
|
||||||
|
self::br->name => "bg-primary",
|
||||||
|
self::newsletter->name => "bg-warning",
|
||||||
|
self::qrcode->name => "bg-secondary",
|
||||||
|
self::advice->name => "bg-info",
|
||||||
|
self::complaints->name => "bg-dark",
|
||||||
|
self::mailing->name => "bg-discovery",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Class
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function class(): string
|
||||||
|
{
|
||||||
|
return self::CLASSES[$this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enum classes
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function toClasses(): array
|
||||||
|
{
|
||||||
|
return self::CLASSES;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Enums/Users/RoleEnum.php
Normal file
23
app/Enums/Users/RoleEnum.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums\Users;
|
||||||
|
|
||||||
|
use App\Traits\Enums\BaseEnum;
|
||||||
|
|
||||||
|
enum RoleEnum: int
|
||||||
|
{
|
||||||
|
use BaseEnum;
|
||||||
|
|
||||||
|
case developper = 0;
|
||||||
|
case admin = 1;
|
||||||
|
case client = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionnal labels definition
|
||||||
|
*/
|
||||||
|
private const LABELS = [
|
||||||
|
self::developper->name => "Developpeur",
|
||||||
|
self::admin->name => "Administrateur",
|
||||||
|
self::client->name => "Client",
|
||||||
|
];
|
||||||
|
}
|
||||||
30
app/Events/BrevoWebhookCallReceived.php
Normal file
30
app/Events/BrevoWebhookCallReceived.php
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Domain\Models\ValueObjects\Email;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
|
||||||
|
readonly class BrevoWebhookCallReceived
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public Email $from,
|
||||||
|
public Email $to,
|
||||||
|
public string $htmlRawContent
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||||
|
*/
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel('default'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/NotifyAdmin.php
Normal file
34
app/Events/NotifyAdmin.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class NotifyAdmin
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithSockets;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var \App\Models\Ticket */
|
||||||
|
public $ticket;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $corpsMaj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @param string $corpsMaj
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Ticket $ticket, string $corpsMaj)
|
||||||
|
{
|
||||||
|
$this->ticket = $ticket;
|
||||||
|
$this->corpsMaj = $corpsMaj;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Events/NotifySubscriptions.php
Normal file
40
app/Events/NotifySubscriptions.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
class NotifySubscriptions
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithSockets;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var \App\Models\Ticket */
|
||||||
|
public $ticket;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $corpsMaj;
|
||||||
|
|
||||||
|
/** @var Collection */
|
||||||
|
public $ignoreUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @param string $corpsMaj
|
||||||
|
* @param \Illuminate\Support\Collection|null $ignoreUsers
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Ticket $ticket, string $corpsMaj, Collection $ignoreUsers = null)
|
||||||
|
{
|
||||||
|
$this->ticket = $ticket;
|
||||||
|
$this->corpsMaj = $corpsMaj;
|
||||||
|
$this->ignoreUsers = $ignoreUsers;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/NotifyTicketCreator.php
Normal file
34
app/Events/NotifyTicketCreator.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class NotifyTicketCreator
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithSockets;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var \App\Models\Ticket */
|
||||||
|
public $ticket;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $corpsMaj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @param string $corpsMaj
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Ticket $ticket, string $corpsMaj)
|
||||||
|
{
|
||||||
|
$this->ticket = $ticket;
|
||||||
|
$this->corpsMaj = $corpsMaj;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/TicketCreated.php
Normal file
34
app/Events/TicketCreated.php
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class TicketCreated implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public readonly Site $siteReceivingTicket,
|
||||||
|
public readonly Ticket $ticket,
|
||||||
|
public readonly string $byUserName
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||||
|
*/
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel('default'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
62
app/Events/TicketStatusChanged.php
Normal file
62
app/Events/TicketStatusChanged.php
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Enums\Tickets\TicketStatusEnum;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class TicketStatusChanged
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use InteractsWithSockets;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/** @var \App\Models\Site */
|
||||||
|
public $ticket;
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/** @var \App\Enums\Tickets\TicketStatusEnum */
|
||||||
|
public $status;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $message;
|
||||||
|
|
||||||
|
/** @var boolean */
|
||||||
|
public $is_private;
|
||||||
|
|
||||||
|
/** @var boolean */
|
||||||
|
public $status_changed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @param \App\Enums\Tickets\TicketStatusEnum $status
|
||||||
|
* @param \App\Models\User|null $user
|
||||||
|
* @param string|null $message
|
||||||
|
* @param boolean $is_private
|
||||||
|
* @param boolean $status_changed
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
Ticket $ticket,
|
||||||
|
TicketStatusEnum $status,
|
||||||
|
User $user = null,
|
||||||
|
string $message = null,
|
||||||
|
bool $is_private = false,
|
||||||
|
bool $status_changed = true
|
||||||
|
) {
|
||||||
|
$this->ticket = $ticket;
|
||||||
|
$this->status = $status;
|
||||||
|
$this->user = $user;
|
||||||
|
$this->message = $message;
|
||||||
|
$this->is_private = $is_private;
|
||||||
|
$this->status_changed = $status_changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Exceptions/Handler.php
Normal file
47
app/Exceptions/Handler.php
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A list of exception types with their corresponding custom log levels.
|
||||||
|
*
|
||||||
|
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||||
|
*/
|
||||||
|
protected $levels = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the exception types that are not reported.
|
||||||
|
*
|
||||||
|
* @var array<int, class-string<\Throwable>>
|
||||||
|
*/
|
||||||
|
protected $dontReport = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the inputs that are never flashed for validation exceptions.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the exception handling callbacks for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->reportable(function (\Throwable $e) {
|
||||||
|
if (app()->bound('sentry')) {
|
||||||
|
app('sentry')->captureException($e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
|
||||||
|
|
||||||
use App\Http\Requests\Auth\ForgotPasswordRequest;
|
|
||||||
use App\Http\Requests\Auth\LoginRequest;
|
|
||||||
use App\Http\Requests\Auth\ResetPasswordRequest;
|
|
||||||
use App\Mails\Auth\ResetPassword;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\UserPassword;
|
|
||||||
use Illuminate\Auth\Events\PasswordReset;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Routing\Controller;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
use Illuminate\Support\Facades\Password;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Inertia;
|
|
||||||
use Inertia\Response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages backend authentication sessions (login/logout).
|
|
||||||
*/
|
|
||||||
class AuthenticatedSessionController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Display the login view.
|
|
||||||
*/
|
|
||||||
public function login(): Response
|
|
||||||
{
|
|
||||||
return Inertia::render('back/Pages/Auth/Login');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an incoming authentication request.
|
|
||||||
*/
|
|
||||||
public function postLogin(LoginRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$request->authenticate();
|
|
||||||
$request->session()->regenerate();
|
|
||||||
|
|
||||||
return redirect()->intended(route(config('uwil-backend.routes.name') . 'dashboard.index', absolute: false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy an authenticated session.
|
|
||||||
*/
|
|
||||||
public function logout(Request $request): RedirectResponse
|
|
||||||
{
|
|
||||||
Auth::guard('backend')->logout();
|
|
||||||
|
|
||||||
$request->session()->invalidate();
|
|
||||||
$request->session()->regenerateToken();
|
|
||||||
|
|
||||||
return to_route(config('uwil-backend.routes.name') . 'login');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the forgotten password view.
|
|
||||||
*/
|
|
||||||
public function forgotPassword(): Response
|
|
||||||
{
|
|
||||||
return Inertia::render('back/Pages/Auth/ForgotPassword');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an incoming forgotten password request.
|
|
||||||
*/
|
|
||||||
public function postForgotPassword(ForgotPasswordRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$status = Password::broker('backend')->sendResetLink(
|
|
||||||
$request->validated()
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($status !== Password::RESET_LINK_SENT) {
|
|
||||||
Log::warning('Password reset link not sent', [
|
|
||||||
'status' => $status,
|
|
||||||
'email' => $request->input('email'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route(config('uwil-backend.routes.name') . 'login');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a reset password link to the user.
|
|
||||||
*/
|
|
||||||
public function sendResetPasswordLink(User $user): RedirectResponse
|
|
||||||
{
|
|
||||||
$status = Password::broker('backend')->sendResetLink(['email' => $user->email]);
|
|
||||||
|
|
||||||
return $status === Password::RESET_LINK_SENT
|
|
||||||
? back()
|
|
||||||
: back()->withErrors(['email' => trans('auth.throttled')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the view.
|
|
||||||
*/
|
|
||||||
public function resetPassword(string $token): Response
|
|
||||||
{
|
|
||||||
return Inertia::render('back/Pages/Auth/ResetPassword', [
|
|
||||||
'token' => $token,
|
|
||||||
'email' => request()->query('email', ''),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle an incoming resetting password request.
|
|
||||||
*/
|
|
||||||
public function postResetPassword(ResetPasswordRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$status = Password::broker('backend')->reset(
|
|
||||||
$request->validated(),
|
|
||||||
function (User $user, string $password): void {
|
|
||||||
$user->setRememberToken(Str::random(60));
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
UserPassword::updateOrCreate(
|
|
||||||
['user_id' => $user->getKey()],
|
|
||||||
['password' => Hash::make($password)],
|
|
||||||
);
|
|
||||||
|
|
||||||
event(new PasswordReset($user));
|
|
||||||
|
|
||||||
Mail::send(new ResetPassword($user));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return $status === Password::PASSWORD_RESET
|
|
||||||
? to_route(config('uwil-backend.routes.name') . 'login')
|
|
||||||
: back()->withErrors(['email' => trans('auth.token')]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use App\Http\Requests\Back\Brands\StoreBrandRequest;
|
|
||||||
use App\Http\Requests\Back\Brands\UpdateBrandRequest;
|
|
||||||
use App\Models\Brand;
|
|
||||||
use App\Models\Site;
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Uwil\LaravelBackend\Controllers\BackendController;
|
|
||||||
use Uwil\LaravelBackend\Resources\CommonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for Brand in the backend.
|
|
||||||
*/
|
|
||||||
class BrandController extends BackendController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gitlab projects collection.
|
|
||||||
*
|
|
||||||
* @var \Illuminate\Support\Collection<int,\App\Models\Site>
|
|
||||||
*/
|
|
||||||
private readonly Collection $siteModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the controller instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->authorizeResource(Brand::class);
|
|
||||||
$this->modelName = Brand::modelName();
|
|
||||||
$this->modelPage = Brand::modelPage();
|
|
||||||
$this->siteModels = Site::getModelsCached();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request): Response
|
|
||||||
{
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\Brand> $query */
|
|
||||||
$query = Brand::query();
|
|
||||||
|
|
||||||
if ($request->routeIs("{$this->routeName}{$this->modelName}.trash")) {
|
|
||||||
$query = $query->onlyTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $search Search field */
|
|
||||||
$search = $request->search;
|
|
||||||
if ($search) {
|
|
||||||
$this->searchQuery(
|
|
||||||
$query,
|
|
||||||
$search,
|
|
||||||
null,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$searchFields = \implode(', ', [
|
|
||||||
trans('validation.attributes.name'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Sort columns with a query */
|
|
||||||
$this->sortQuery($query);
|
|
||||||
|
|
||||||
/** Custom pagination */
|
|
||||||
$models = $this->paginate($query);
|
|
||||||
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Index", [
|
|
||||||
'models' => CommonResource::collection($models),
|
|
||||||
'search' => $search,
|
|
||||||
'searchFields' => $searchFields,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create(Brand $brand): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Create", [
|
|
||||||
'data' => CommonResource::make($brand),
|
|
||||||
'siteModels' => $this->siteModels,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a specific resource.
|
|
||||||
*/
|
|
||||||
public function show(Brand $brand): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Show", [
|
|
||||||
'data' => CommonResource::make($brand->load([
|
|
||||||
'sites' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
])),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource.
|
|
||||||
*/
|
|
||||||
public function store(StoreBrandRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($request) {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$brand = new Brand();
|
|
||||||
$brand->fill(Arr::except($validatedData, 'sites'));
|
|
||||||
|
|
||||||
if ($brand->saveOrFail()) {
|
|
||||||
$brand->sites()->syncWithoutDetaching(Arr::get($validatedData, 'sites'));
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $brand)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(Brand $brand): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Edit", [
|
|
||||||
'data' => CommonResource::make($brand->load(['sites'])),
|
|
||||||
'siteModels' => $this->siteModels,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
UpdateBrandRequest $request,
|
|
||||||
Brand $brand,
|
|
||||||
): RedirectResponse {
|
|
||||||
return DB::transaction(function () use ($request, $brand) {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$brand->fill(Arr::except($validatedData, 'sites'));
|
|
||||||
|
|
||||||
if ($brand->saveOrFail()) {
|
|
||||||
$brand->sites()->sync(Arr::get($validatedData, 'sites'));
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $brand)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $brand)
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the specified resource.
|
|
||||||
*/
|
|
||||||
public function duplicate(Brand $brand): Response
|
|
||||||
{
|
|
||||||
$brand->loadMissing(Brand::getCacheRelations());
|
|
||||||
$replica = $brand->replicate();
|
|
||||||
collect($brand->getRelations())->each(fn ($value, $relation) => $replica->setRelation($relation, $value));
|
|
||||||
|
|
||||||
return $this->create($replica);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the specified resource.
|
|
||||||
*/
|
|
||||||
public function restore(int $brand): RedirectResponse
|
|
||||||
{
|
|
||||||
$brand = Brand::withTrashed()->findOrFail($brand);
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($brand->restore()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function destroy(Brand $brand): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($brand->delete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.index", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function forceDestroy(Brand | int $brand): RedirectResponse
|
|
||||||
{
|
|
||||||
if (is_int($brand)) {
|
|
||||||
$brand = Brand::withTrashed()->findOrFail($brand);
|
|
||||||
}
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($brand->forceDelete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use App\Http\Requests\Back\InvoiceCompanies\StoreInvoiceCompanyRequest;
|
|
||||||
use App\Http\Requests\Back\InvoiceCompanies\UpdateInvoiceCompanyRequest;
|
|
||||||
use App\Models\InvoiceCompany;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Uwil\LaravelBackend\Controllers\BackendController;
|
|
||||||
use Uwil\LaravelBackend\Resources\CommonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for InvoiceCompany in the backend.
|
|
||||||
*/
|
|
||||||
class InvoiceCompanyController extends BackendController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create the controller instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->authorizeResource(InvoiceCompany::class);
|
|
||||||
$this->modelName = InvoiceCompany::modelName();
|
|
||||||
$this->modelPage = InvoiceCompany::modelPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request): Response
|
|
||||||
{
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\InvoiceCompany> $query */
|
|
||||||
$query = InvoiceCompany::query();
|
|
||||||
|
|
||||||
if ($request->routeIs("{$this->routeName}{$this->modelName}.trash")) {
|
|
||||||
$query = $query->onlyTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $search Search field */
|
|
||||||
$search = $request->search;
|
|
||||||
if ($search) {
|
|
||||||
$this->searchQuery(
|
|
||||||
$query,
|
|
||||||
$search,
|
|
||||||
null,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$searchFields = \implode(', ', [
|
|
||||||
trans('validation.attributes.name'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Sort columns with a query */
|
|
||||||
$this->sortQuery($query);
|
|
||||||
|
|
||||||
/** Custom pagination */
|
|
||||||
$models = $this->paginate($query);
|
|
||||||
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Index", [
|
|
||||||
'models' => CommonResource::collection($models),
|
|
||||||
'search' => $search,
|
|
||||||
'searchFields' => $searchFields,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create(InvoiceCompany $invoice_company): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Create", [
|
|
||||||
'data' => CommonResource::make($invoice_company),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a specific resource.
|
|
||||||
*/
|
|
||||||
public function show(InvoiceCompany $invoice_company): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Show", [
|
|
||||||
'data' => CommonResource::make($invoice_company),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource.
|
|
||||||
*/
|
|
||||||
public function store(StoreInvoiceCompanyRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($request) {
|
|
||||||
$invoice_company = new InvoiceCompany();
|
|
||||||
$invoice_company->fill($request->validated());
|
|
||||||
|
|
||||||
if ($invoice_company->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_company)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(InvoiceCompany $invoice_company): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Edit", [
|
|
||||||
'data' => CommonResource::make($invoice_company),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
UpdateInvoiceCompanyRequest $request,
|
|
||||||
InvoiceCompany $invoice_company
|
|
||||||
): RedirectResponse {
|
|
||||||
return DB::transaction(function () use ($request, $invoice_company) {
|
|
||||||
$invoice_company->fill($request->validated());
|
|
||||||
|
|
||||||
if ($invoice_company->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_company)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_company)
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the specified resource.
|
|
||||||
*/
|
|
||||||
public function duplicate(InvoiceCompany $invoice_company): Response
|
|
||||||
{
|
|
||||||
$invoice_company->loadMissing(InvoiceCompany::getCacheRelations());
|
|
||||||
$replica = $invoice_company->replicate();
|
|
||||||
collect($invoice_company->getRelations())
|
|
||||||
->each(fn ($value, $relation) => $replica->setRelation($relation, $value));
|
|
||||||
|
|
||||||
return $this->create($replica);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the specified resource.
|
|
||||||
*/
|
|
||||||
public function restore(int $invoice_company): RedirectResponse
|
|
||||||
{
|
|
||||||
$invoice_company = InvoiceCompany::withTrashed()->findOrFail($invoice_company);
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_company->restore()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function destroy(InvoiceCompany $invoice_company): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_company->delete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.index", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function forceDestroy(InvoiceCompany | int $invoice_company): RedirectResponse
|
|
||||||
{
|
|
||||||
if (is_int($invoice_company)) {
|
|
||||||
$invoice_company = InvoiceCompany::withTrashed()->findOrFail($invoice_company);
|
|
||||||
}
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_company->forceDelete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,229 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use App\Http\Requests\Back\InvoicePackages\StoreInvoicePackageRequest;
|
|
||||||
use App\Http\Requests\Back\InvoicePackages\UpdateInvoicePackageRequest;
|
|
||||||
use App\Models\InvoicePackage;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Uwil\LaravelBackend\Controllers\BackendController;
|
|
||||||
use Uwil\LaravelBackend\Resources\CommonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for InvoicePackage in the backend.
|
|
||||||
*/
|
|
||||||
class InvoicePackageController extends BackendController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create the controller instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->authorizeResource(InvoicePackage::class);
|
|
||||||
$this->modelName = InvoicePackage::modelName();
|
|
||||||
$this->modelPage = InvoicePackage::modelPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request): Response
|
|
||||||
{
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\InvoicePackage> $query */
|
|
||||||
$query = InvoicePackage::query();
|
|
||||||
|
|
||||||
if ($request->routeIs("{$this->routeName}{$this->modelName}.trash")) {
|
|
||||||
$query = $query->onlyTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $search Search field */
|
|
||||||
$search = $request->search;
|
|
||||||
if ($search) {
|
|
||||||
$this->searchQuery(
|
|
||||||
$query,
|
|
||||||
$search,
|
|
||||||
null,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$searchFields = \implode(', ', [
|
|
||||||
trans('validation.attributes.name'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Sort columns with a query */
|
|
||||||
$this->sortQuery($query);
|
|
||||||
|
|
||||||
/** Custom pagination */
|
|
||||||
$models = $this->paginate($query);
|
|
||||||
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Index", [
|
|
||||||
'models' => CommonResource::collection($models),
|
|
||||||
'search' => $search,
|
|
||||||
'searchFields' => $searchFields,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create(InvoicePackage $invoice_package): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Create", [
|
|
||||||
'data' => CommonResource::make($invoice_package),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a specific resource.
|
|
||||||
*/
|
|
||||||
public function show(InvoicePackage $invoice_package): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Show", [
|
|
||||||
'data' => CommonResource::make($invoice_package),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource.
|
|
||||||
*/
|
|
||||||
public function store(StoreInvoicePackageRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($request) {
|
|
||||||
$invoice_package = new InvoicePackage();
|
|
||||||
$invoice_package->fill($request->validated());
|
|
||||||
|
|
||||||
if ($invoice_package->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_package)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(InvoicePackage $invoice_package): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Edit", [
|
|
||||||
'data' => CommonResource::make($invoice_package),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
UpdateInvoicePackageRequest $request,
|
|
||||||
InvoicePackage $invoice_package
|
|
||||||
): RedirectResponse {
|
|
||||||
return DB::transaction(function () use ($request, $invoice_package) {
|
|
||||||
$invoice_package->fill($request->validated());
|
|
||||||
|
|
||||||
if ($invoice_package->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_package)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $invoice_package)
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the specified resource.
|
|
||||||
*/
|
|
||||||
public function duplicate(InvoicePackage $invoice_package): Response
|
|
||||||
{
|
|
||||||
$invoice_package->loadMissing(InvoicePackage::getCacheRelations());
|
|
||||||
$replica = $invoice_package->replicate();
|
|
||||||
collect($invoice_package->getRelations())
|
|
||||||
->each(fn ($value, $relation) => $replica->setRelation($relation, $value));
|
|
||||||
|
|
||||||
return $this->create($replica);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the specified resource.
|
|
||||||
*/
|
|
||||||
public function restore(int $invoice_package): RedirectResponse
|
|
||||||
{
|
|
||||||
$invoice_package = InvoicePackage::withTrashed()->findOrFail($invoice_package);
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_package->restore()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function destroy(InvoicePackage $invoice_package): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_package->delete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.index", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function forceDestroy(InvoicePackage | int $invoice_package): RedirectResponse
|
|
||||||
{
|
|
||||||
if (is_int($invoice_package)) {
|
|
||||||
$invoice_package = InvoicePackage::withTrashed()->findOrFail($invoice_package);
|
|
||||||
}
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($invoice_package->forceDelete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use App\Http\Requests\Back\Notices\StoreNoticeRequest;
|
|
||||||
use App\Http\Requests\Back\Notices\UpdateNoticeRequest;
|
|
||||||
use App\Models\Notice;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Uwil\LaravelBackend\Controllers\BackendController;
|
|
||||||
use Uwil\LaravelBackend\Resources\CommonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for Notice in the backend.
|
|
||||||
*/
|
|
||||||
class NoticeController extends BackendController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create the controller instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->authorizeResource(Notice::class);
|
|
||||||
$this->modelName = Notice::modelName();
|
|
||||||
$this->modelPage = Notice::modelPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request): Response
|
|
||||||
{
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\Notice> $query */
|
|
||||||
$query = Notice::query();
|
|
||||||
|
|
||||||
if ($request->routeIs("{$this->routeName}{$this->modelName}.trash")) {
|
|
||||||
$query = $query->onlyTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $search Search field */
|
|
||||||
$search = $request->search;
|
|
||||||
if ($search) {
|
|
||||||
$this->searchQuery(
|
|
||||||
$query,
|
|
||||||
$search,
|
|
||||||
null,
|
|
||||||
'title',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$searchFields = \implode(', ', [
|
|
||||||
trans('validation.attributes.title'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Sort columns with a query */
|
|
||||||
$this->sortQuery($query);
|
|
||||||
|
|
||||||
/** Custom pagination */
|
|
||||||
$models = $this->paginate($query);
|
|
||||||
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Index", [
|
|
||||||
'models' => CommonResource::collection($models),
|
|
||||||
'search' => $search,
|
|
||||||
'searchFields' => $searchFields,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create(Notice $notice): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Create", [
|
|
||||||
'data' => CommonResource::make($notice),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a specific resource.
|
|
||||||
*/
|
|
||||||
public function show(Notice $notice): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Show", [
|
|
||||||
'data' => CommonResource::make($notice),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource.
|
|
||||||
*/
|
|
||||||
public function store(StoreNoticeRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($request) {
|
|
||||||
$notice = new Notice();
|
|
||||||
$notice->fill($request->validated());
|
|
||||||
|
|
||||||
if ($notice->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $notice)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(Notice $notice): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Edit", [
|
|
||||||
'data' => CommonResource::make($notice),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
UpdateNoticeRequest $request,
|
|
||||||
Notice $notice
|
|
||||||
): RedirectResponse {
|
|
||||||
return DB::transaction(function () use ($request, $notice) {
|
|
||||||
$notice->fill($request->validated());
|
|
||||||
|
|
||||||
if ($notice->saveOrFail()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $notice)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $notice)
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the specified resource.
|
|
||||||
*/
|
|
||||||
public function duplicate(Notice $notice): Response
|
|
||||||
{
|
|
||||||
$notice->loadMissing(Notice::getCacheRelations());
|
|
||||||
$replica = $notice->replicate();
|
|
||||||
collect($notice->getRelations())->each(fn ($value, $relation) => $replica->setRelation($relation, $value));
|
|
||||||
|
|
||||||
return $this->create($replica);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the specified resource.
|
|
||||||
*/
|
|
||||||
public function restore(int $notice): RedirectResponse
|
|
||||||
{
|
|
||||||
$notice = Notice::withTrashed()->findOrFail($notice);
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($notice->restore()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function destroy(Notice $notice): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($notice->delete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.index", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function forceDestroy(Notice | int $notice): RedirectResponse
|
|
||||||
{
|
|
||||||
if (is_int($notice)) {
|
|
||||||
$notice = Notice::withTrashed()->findOrFail($notice);
|
|
||||||
}
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($notice->forceDelete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,287 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use App\Http\Requests\Back\Sites\StoreSiteRequest;
|
|
||||||
use App\Http\Requests\Back\Sites\UpdateSiteRequest;
|
|
||||||
use App\Models\Site;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Uwil\LaravelBackend\Controllers\BackendController;
|
|
||||||
use Uwil\LaravelBackend\Resources\CommonResource;
|
|
||||||
use Uwil\LaravelBackend\Support\UserRoleRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for Site in the backend.
|
|
||||||
*/
|
|
||||||
class SiteController extends BackendController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Project managers collection.
|
|
||||||
*
|
|
||||||
* @var \Illuminate\Support\Collection<int,\App\Models\User>
|
|
||||||
*/
|
|
||||||
private readonly Collection $userModelsProjectManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Project managers collection.
|
|
||||||
*
|
|
||||||
* @var \Illuminate\Support\Collection<int,\App\Models\User>
|
|
||||||
*/
|
|
||||||
private readonly Collection $userModelsClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the controller instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->authorizeResource(Site::class);
|
|
||||||
$this->modelName = Site::modelName();
|
|
||||||
$this->modelPage = Site::modelPage();
|
|
||||||
|
|
||||||
/** @var \Illuminate\Support\Collection<int, \App\Models\User> $userModels */
|
|
||||||
$userModels = User::getModelsCached()
|
|
||||||
->where('published', true)
|
|
||||||
->where('deleted_at', null)
|
|
||||||
->values();
|
|
||||||
$this->userModelsProjectManager = $userModels
|
|
||||||
->filter(fn (User $user) => $user->role->value === UserRoleRegistry::get('project-manager')->value)
|
|
||||||
->values();
|
|
||||||
$this->userModelsClient = $userModels
|
|
||||||
->filter(fn (User $user) => $user->role->value === UserRoleRegistry::get('client')->value)
|
|
||||||
->values();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(Request $request): Response
|
|
||||||
{
|
|
||||||
/** @var \App\Models\User $userModel */
|
|
||||||
$userModel = auth('backend')->user();
|
|
||||||
|
|
||||||
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\Site> $query */
|
|
||||||
$query = Site::query()
|
|
||||||
->when(
|
|
||||||
$userModel->role->value === UserRoleRegistry::get('client')->value,
|
|
||||||
fn ($query) => $query->whereIn('id', $userModel->sites->modelKeys()),
|
|
||||||
)
|
|
||||||
->with(['projectManager', 'gitlabProject']);
|
|
||||||
|
|
||||||
if ($request->routeIs("{$this->routeName}{$this->modelName}.trash")) {
|
|
||||||
$query = $query->onlyTrashed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $search Search field */
|
|
||||||
$search = $request->search;
|
|
||||||
if ($search) {
|
|
||||||
$this->searchQuery(
|
|
||||||
$query,
|
|
||||||
$search,
|
|
||||||
null,
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$searchFields = \implode(', ', [
|
|
||||||
trans('validation.attributes.name'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** Sort columns with a query */
|
|
||||||
$this->sortQuery($query);
|
|
||||||
|
|
||||||
/** Custom pagination */
|
|
||||||
$models = $this->paginate($query);
|
|
||||||
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Index", [
|
|
||||||
'models' => CommonResource::collection($models),
|
|
||||||
'search' => $search,
|
|
||||||
'searchFields' => $searchFields,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create(Site $site): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Create", [
|
|
||||||
'data' => CommonResource::make($site),
|
|
||||||
'userModelsProjectManager' => $this->userModelsProjectManager,
|
|
||||||
'userModelsClient' => $this->userModelsClient,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a specific resource.
|
|
||||||
*/
|
|
||||||
public function show(Site $site): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Show", [
|
|
||||||
'data' => CommonResource::make($site->load([
|
|
||||||
'projectManager' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
'gitlabProject' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
'users' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
])),
|
|
||||||
'userModelsProjectManager' => $this->userModelsProjectManager,
|
|
||||||
'userModelsClient' => $this->userModelsClient,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource.
|
|
||||||
*/
|
|
||||||
public function store(StoreSiteRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
return DB::transaction(function () use ($request) {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$site = new Site();
|
|
||||||
$site->fill(Arr::except($validatedData, 'users'));
|
|
||||||
|
|
||||||
if ($site->saveOrFail()) {
|
|
||||||
$site->users()->syncWithoutDetaching(Arr::get($validatedData, 'users'));
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $site)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_created', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(Site $site): Response
|
|
||||||
{
|
|
||||||
return $this->inertiaRender("back/Pages/$this->modelPage/Edit", [
|
|
||||||
'data' => CommonResource::make($site->load([
|
|
||||||
'projectManager' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
'gitlabProject' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
'users' => fn ($q) => $q->withoutGlobalScope(SoftDeletingScope::class),
|
|
||||||
])),
|
|
||||||
'userModelsProjectManager' => $this->userModelsProjectManager,
|
|
||||||
'userModelsClient' => $this->userModelsClient,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
public function update(
|
|
||||||
UpdateSiteRequest $request,
|
|
||||||
Site $site
|
|
||||||
): RedirectResponse {
|
|
||||||
return DB::transaction(function () use ($request, $site) {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$site->fill(Arr::except($validatedData, 'users'));
|
|
||||||
|
|
||||||
if ($site->saveOrFail()) {
|
|
||||||
$site->users()->sync(Arr::get($validatedData, 'users'));
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $site)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.edit", $site)
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_updated', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate the specified resource.
|
|
||||||
*/
|
|
||||||
public function duplicate(Site $site): Response
|
|
||||||
{
|
|
||||||
$site->loadMissing(Site::getCacheRelations());
|
|
||||||
$replica = $site->replicate();
|
|
||||||
collect($site->getRelations())->each(fn ($value, $relation) => $replica->setRelation($relation, $value));
|
|
||||||
|
|
||||||
return $this->create($replica);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the specified resource.
|
|
||||||
*/
|
|
||||||
public function restore(int $site): RedirectResponse
|
|
||||||
{
|
|
||||||
$site = Site::withTrashed()->findOrFail($site);
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($site->restore()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_restored', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function destroy(Site $site): RedirectResponse
|
|
||||||
{
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($site->delete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.index", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the specified resource.
|
|
||||||
*/
|
|
||||||
public function forceDestroy(Site | int $site): RedirectResponse
|
|
||||||
{
|
|
||||||
if (is_int($site)) {
|
|
||||||
$site = Site::withTrashed()->findOrFail($site);
|
|
||||||
}
|
|
||||||
/** @var array<string,string> */
|
|
||||||
$previousQueries = [];
|
|
||||||
\parse_str((string) \parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
|
|
||||||
if ($site->forceDelete()) {
|
|
||||||
return to_route("{$this->routeName}{$this->modelName}.trash", $previousQueries)
|
|
||||||
->with('success', trans('uwil-backend::crud.messages.has_been_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return back()
|
|
||||||
->with('error', trans('uwil-backend::crud.messages.cannot_be_deleted', [
|
|
||||||
'model' => Str::ucfirst(trans_choice("models.classes.$this->modelName", 1)),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Back;
|
|
||||||
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Uwil\LaravelBackend\Controllers\UserController as BackendUserController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles CRUD operations for Site in the backend.
|
|
||||||
*/
|
|
||||||
class UserController extends BackendUserController
|
|
||||||
{
|
|
||||||
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function store(
|
|
||||||
Request $request,
|
|
||||||
mixed $callback = null,
|
|
||||||
): RedirectResponse {
|
|
||||||
return parent::store($request, function ($request, $user): void {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$user->sites()->syncWithoutDetaching(Arr::get($validatedData, 'sites'));
|
|
||||||
if (Arr::get($validatedData, 'gitlab_id')) {
|
|
||||||
$user->gitlabUser()->create([
|
|
||||||
'gitlab_id' => Arr::get($validatedData, 'gitlab_id'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function update(
|
|
||||||
Request $request,
|
|
||||||
mixed $user,
|
|
||||||
mixed $callback = null,
|
|
||||||
): RedirectResponse {
|
|
||||||
return parent::update($request, $user, function ($request, $user): void {
|
|
||||||
$validatedData = $request->validated();
|
|
||||||
$user->sites()->sync(Arr::get($validatedData, 'sites'));
|
|
||||||
if (Arr::get($validatedData, 'gitlab_id')) {
|
|
||||||
$user->gitlabUser()->updateOrCreate(
|
|
||||||
['user_id' => $user->id],
|
|
||||||
['gitlab_id' => $validatedData['gitlab_id']],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$user->gitlabUser()->delete();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:enable
|
|
||||||
}
|
|
||||||
29
app/Http/Controllers/Bo/Auth/ConfirmPasswordController.php
Normal file
29
app/Http/Controllers/Bo/Auth/ConfirmPasswordController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
||||||
|
|
||||||
|
class ConfirmPasswordController extends Controller
|
||||||
|
{
|
||||||
|
use ConfirmsPasswords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users when the intended url fails.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = RouteServiceProvider::HOME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Http/Controllers/Bo/Auth/ForgotPasswordController.php
Normal file
21
app/Http/Controllers/Bo/Auth/ForgotPasswordController.php
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
|
|
||||||
|
class ForgotPasswordController extends Controller
|
||||||
|
{
|
||||||
|
use SendsPasswordResetEmails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ForgotPasswordController
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('guest:backend');
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Http/Controllers/Bo/Auth/LoginController.php
Normal file
29
app/Http/Controllers/Bo/Auth/LoginController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
|
||||||
|
class LoginController extends Controller
|
||||||
|
{
|
||||||
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after login.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = RouteServiceProvider::HOME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('guest:backend')->except('logout');
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Http/Controllers/Bo/Auth/ResetPasswordController.php
Normal file
41
app/Http/Controllers/Bo/Auth/ResetPasswordController.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
|
||||||
|
class ResetPasswordController extends Controller
|
||||||
|
{
|
||||||
|
use ResetsPasswords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after resetting their password.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = RouteServiceProvider::HOME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the password reset validation rules.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'token' => 'required',
|
||||||
|
'email' => 'required|email',
|
||||||
|
'password' => [
|
||||||
|
'required', 'confirmed', Password::min(8)
|
||||||
|
->mixedCase()
|
||||||
|
->letters()
|
||||||
|
->numbers()
|
||||||
|
->symbols()
|
||||||
|
->uncompromised()
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
186
app/Http/Controllers/Bo/BrandController.php
Normal file
186
app/Http/Controllers/Bo/BrandController.php
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Brands\StoreBrandRequest;
|
||||||
|
use App\Http\Requests\Bo\Brands\UpdateBrandRequest;
|
||||||
|
use App\Models\Brand;
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class BrandController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $brandModels */
|
||||||
|
$brandModels = Brand::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$brandModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($brandModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$brandModels = $this->customPaginate($brandModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.brands.index', compact('brandModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show model action.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function show(Brand $brand): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.brands.show', ['brandModel' => $brand]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Brand $brand): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.brands.create', [
|
||||||
|
'brandModel' => $brand,
|
||||||
|
'siteModels' => $siteModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Brands\StoreBrandRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreBrandRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$brand = new Brand();
|
||||||
|
$brand->fill($request->safe()->except('sites'));
|
||||||
|
|
||||||
|
if ($brand->saveOrFail()) {
|
||||||
|
$brand->updateSites($brand);
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.edit', $brand)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Brand $brand): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.brands.edit', [
|
||||||
|
'brandModel' => $brand,
|
||||||
|
'siteModels' => $siteModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Brands\UpdateBrandRequest $request
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateBrandRequest $request, Brand $brand): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $brand) {
|
||||||
|
$brand->fill($request->safe()->except(['sites']));
|
||||||
|
$brand->updateSites($brand);
|
||||||
|
|
||||||
|
if ($brand->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.edit', $brand)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Brand $brand): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($brand) {
|
||||||
|
if (count($brand->accounts)) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.index')
|
||||||
|
->with('error', __(
|
||||||
|
':model n\'as pas pu être supprimé car elle est encore associée avec des Sites.',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$brand->deleteSites($brand);
|
||||||
|
if ($brand->deleteOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.index')
|
||||||
|
->with('success', __(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', __(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.brands'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Http/Controllers/Bo/BrevoWebhookController.php
Normal file
58
app/Http/Controllers/Bo/BrevoWebhookController.php
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Events\BrevoWebhookCallReceived;
|
||||||
|
use App\Http\Requests\Bo\BrevoWebhookRequest;
|
||||||
|
use Domain\Models\ValueObjects\Email;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use \Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
|
class BrevoWebhookController extends BaseController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
private readonly Dispatcher $dispatcher
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function webhookCallReceived(BrevoWebhookRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
$fromEmail = $request->getFromAddress();
|
||||||
|
$toEmail = $request->getToAddress();
|
||||||
|
$formContent = $request->getRawHtmlBody();
|
||||||
|
|
||||||
|
if (!$this->isFromLdc($fromEmail)) {
|
||||||
|
$this->logger->warning(
|
||||||
|
'Received a brevo webhook call from a non ldc email.',
|
||||||
|
[
|
||||||
|
'from' => $fromEmail,
|
||||||
|
'to' => $toEmail,
|
||||||
|
'content' => base64_encode($formContent)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
['ok' => false],
|
||||||
|
Response::HTTP_BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(
|
||||||
|
new BrevoWebhookCallReceived(
|
||||||
|
from: $fromEmail,
|
||||||
|
to: $toEmail,
|
||||||
|
htmlRawContent: $formContent,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(['ok' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isFromLdc(Email $email): bool
|
||||||
|
{
|
||||||
|
return substr(strrchr($email->value, "@"), 1) === 'ldc.fr';
|
||||||
|
}
|
||||||
|
}
|
||||||
166
app/Http/Controllers/Bo/CompanyController.php
Normal file
166
app/Http/Controllers/Bo/CompanyController.php
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Companies\StoreCompanyRequest;
|
||||||
|
use App\Http\Requests\Bo\Companies\UpdateCompanyRequest;
|
||||||
|
use App\Models\Company;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class CompanyController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $companyModels */
|
||||||
|
$companyModels = Company::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$companyModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($companyModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$companyModels = $this->customPaginate($companyModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.companies.index', compact('companyModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Company $company
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Company $company): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.companies.create', [
|
||||||
|
'companyModel' => $company,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Companies\StoreCompanyRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreCompanyRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$company = new Company();
|
||||||
|
$company->fill($request->validated());
|
||||||
|
|
||||||
|
if ($company->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.companies.edit', $company)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Company $company
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Company $company): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.companies.edit', [
|
||||||
|
'companyModel' => $company
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Companies\UpdateCompanyRequest $request
|
||||||
|
* @param \App\Models\Company $company
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateCompanyRequest $request, Company $company): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $company) {
|
||||||
|
$company->fill($request->validated());
|
||||||
|
if ($company->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.companies.edit', $company)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Company $company
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Company $company): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($company) {
|
||||||
|
if (count($company->invoices()->get()->toArray())) {
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_deleted_relationship',
|
||||||
|
[
|
||||||
|
'model' => Str::singular(trans('models.classes.companies')),
|
||||||
|
'relationship' => trans('models.classes.invoices')
|
||||||
|
]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if ($company->deleteOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.companies.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.companies'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
107
app/Http/Controllers/Bo/Credit/AccountController.php
Normal file
107
app/Http/Controllers/Bo/Credit/AccountController.php
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Credit;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\CreditAccounts\StoreCreditAccountRequest;
|
||||||
|
use App\Models\Brand;
|
||||||
|
use App\Models\Credit\Account;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class AccountController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if (!$user->isAdmin()) {
|
||||||
|
$brandModels = $user->brands()->pluck('id');
|
||||||
|
} else {
|
||||||
|
$brandModels = $request->brand ?? Brand::all()->pluck('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $credit_accountModels */
|
||||||
|
$credit_accountModels = Account::query()->whereIn('brand_id', $brandModels);
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$credit_accountModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'label',
|
||||||
|
'balance',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Label, solde');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($credit_accountModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$credit_accountModels = $this->customPaginate($credit_accountModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.credit_accounts.index', compact(
|
||||||
|
'credit_accountModels',
|
||||||
|
'brandModels',
|
||||||
|
'search',
|
||||||
|
'searchFields'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @param \App\Models\Account $account
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Brand $brand, Account $account): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.credit_accounts.create', [
|
||||||
|
'brandModel' => $brand,
|
||||||
|
'credit_accountModel' => $account
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @param \App\Http\Requests\Bo\CreditAccounts\StoreCreditAccountRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(Brand $brand, StoreCreditAccountRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($brand, $request) {
|
||||||
|
$credit_accountModel = new Account();
|
||||||
|
$credit_accountModel->brand_id = $brand->id;
|
||||||
|
$credit_accountModel->fill($request->validated());
|
||||||
|
|
||||||
|
if ($credit_accountModel->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.credit_accounts.index', $brand)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.accounts'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.accounts'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
108
app/Http/Controllers/Bo/Credit/CreditLogController.php
Normal file
108
app/Http/Controllers/Bo/Credit/CreditLogController.php
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Credit;
|
||||||
|
|
||||||
|
use App\Enums\Credit\BalanceOperationEnum;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\CreditLogs\StoreCreditLogRequest;
|
||||||
|
use App\Models\Brand;
|
||||||
|
use App\Models\Credit\Account;
|
||||||
|
use App\Models\Credit\CreditLog;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class CreditLogController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$brandModel = $request->brand;
|
||||||
|
$credit_accountModel = $request->account;
|
||||||
|
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $credit_logModels */
|
||||||
|
$credit_logModels = CreditLog::query()->where('credit_account_id', $credit_accountModel->id);
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$credit_logModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'amount',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Montant');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($credit_logModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$credit_logModels = $this->customPaginate($credit_logModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.credit_logs.index', compact(
|
||||||
|
'credit_logModels',
|
||||||
|
'credit_accountModel',
|
||||||
|
'brandModel',
|
||||||
|
'search',
|
||||||
|
'searchFields'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @param \App\Models\Account $account
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Brand $brand, Account $account): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.credit_logs.create', [
|
||||||
|
'brandModel' => $brand,
|
||||||
|
'credit_accountModel' => $account
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Brand $brand
|
||||||
|
* @param \App\Models\Account $account
|
||||||
|
* @param \App\Http\Requests\Bo\CreditLogs\StoreCreditLogRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(
|
||||||
|
Brand $brand,
|
||||||
|
Account $account,
|
||||||
|
StoreCreditLogRequest $request
|
||||||
|
): \Illuminate\Http\RedirectResponse {
|
||||||
|
return DB::transaction(function () use ($brand, $account, $request) {
|
||||||
|
$credit_logModel = new CreditLog();
|
||||||
|
$credit_logModel->credit_account_id = $request->account->id;
|
||||||
|
$credit_logModel->type = BalanceOperationEnum::credit;
|
||||||
|
$credit_logModel->fill($request->validated());
|
||||||
|
|
||||||
|
if ($credit_logModel->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.brands.credit_accounts.index', ['brand' => $brand, 'account' => $account])
|
||||||
|
->with('success', trans(
|
||||||
|
':model a été crédité',
|
||||||
|
['model' => Str::singular(trans('models.classes.accounts'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':model ne peut être crédité',
|
||||||
|
['model' => Str::singular(trans('models.classes.accounts'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
122
app/Http/Controllers/Bo/Credit/EstimationController.php
Normal file
122
app/Http/Controllers/Bo/Credit/EstimationController.php
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo\Credit;
|
||||||
|
|
||||||
|
use App\Enums\Credit\EstimationStatusEnum;
|
||||||
|
use App\Enums\Tickets\TicketStatusEnum;
|
||||||
|
use App\Events\NotifyAdmin;
|
||||||
|
use App\Events\NotifySubscriptions;
|
||||||
|
use App\Events\TicketStatusChanged;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Credit\AnswerEstimateRequest;
|
||||||
|
use App\Http\Requests\Bo\Credit\CreateEstimateRequest;
|
||||||
|
use App\Models\Credit\Estimation;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class EstimationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Show estimate Form
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
|
*/
|
||||||
|
public function estimate(Ticket $ticket)
|
||||||
|
{
|
||||||
|
$this->authorize('estimate', $ticket);
|
||||||
|
return view('estimations.create', \compact('ticket'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Ticket estimation
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Credit\CreateEstimateRequest $request
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function estimateSubmit(CreateEstimateRequest $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $ticket) {
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$estimate = new Estimation($request->validated());
|
||||||
|
$estimate->ticket()->associate($ticket);
|
||||||
|
$estimate->estimator()->associate($user);
|
||||||
|
$ticket->status = TicketStatusEnum::waitingValidation;
|
||||||
|
$ticket->saveQuietly();
|
||||||
|
if ($estimate->saveOrFail()) {
|
||||||
|
$message = sprintf(
|
||||||
|
'<b>%s</b> a modifié le statut en <b>%s</b>, Une estimation de %s jours à été ajoutée',
|
||||||
|
$user->name,
|
||||||
|
$ticket->status->label(),
|
||||||
|
\number_format(\floatval($request->amount), 1, ',', '')
|
||||||
|
);
|
||||||
|
event(new TicketStatusChanged($ticket, $ticket->status, $user));
|
||||||
|
event(new NotifySubscriptions($ticket, $message));
|
||||||
|
event(new NotifyAdmin($ticket, $message));
|
||||||
|
return response()->redirectToRoute('bo.ticket.show', $ticket)
|
||||||
|
->with('info', trans('L\'estimation à bien été ajouté'));
|
||||||
|
}
|
||||||
|
return \back();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answer ticket estimation
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Credit\AnswerEstimateRequest $request
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function estimatePendingSubmit(AnswerEstimateRequest $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
/** @var \App\Models\Credit\Estimation */
|
||||||
|
$estimation = $ticket->estimation;
|
||||||
|
if ($request->answer) {
|
||||||
|
$ticket->status = TicketStatusEnum::takenInAccount;
|
||||||
|
$estimation->status = EstimationStatusEnum::accepted;
|
||||||
|
} else {
|
||||||
|
$ticket->status = TicketStatusEnum::archived;
|
||||||
|
$estimation->status = EstimationStatusEnum::refused;
|
||||||
|
}
|
||||||
|
$estimation->validator()->associate($user);
|
||||||
|
if ($estimation->saveOrFail() and $ticket->saveOrFail()) {
|
||||||
|
$message = sprintf(
|
||||||
|
'Une réponse à été fourni par %s pour l\'estimation du ticket n°%d : %s',
|
||||||
|
$user->name,
|
||||||
|
$ticket->id,
|
||||||
|
$estimation->status->label()
|
||||||
|
);
|
||||||
|
event(new NotifySubscriptions($ticket, $message));
|
||||||
|
event(new NotifyAdmin($ticket, $message));
|
||||||
|
return response()->redirectToRoute('bo.ticket.show', $ticket)
|
||||||
|
->with('info', trans('Votre réponse à été enregistrée'));
|
||||||
|
}
|
||||||
|
return \back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle submit and cancel an estimate on ticket
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function estimateCancelSubmit(Ticket $ticket)
|
||||||
|
{
|
||||||
|
$this->authorize('cancelEstimate', $ticket);
|
||||||
|
|
||||||
|
/** @var \App\Models\Credit\Estimation */
|
||||||
|
$estimation = $ticket->estimation;
|
||||||
|
$ticket->status = TicketStatusEnum::estimateTodo;
|
||||||
|
|
||||||
|
if ($estimation->deleteOrFail() and $ticket->saveOrFail()) {
|
||||||
|
return response()->redirectToRoute('bo.ticket.show', $ticket)
|
||||||
|
->with('info', trans('L\'estimation a été supprimée.'));
|
||||||
|
}
|
||||||
|
return \back();
|
||||||
|
}
|
||||||
|
}
|
||||||
158
app/Http/Controllers/Bo/InformationController.php
Normal file
158
app/Http/Controllers/Bo/InformationController.php
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Informations\StoreInformationRequest;
|
||||||
|
use App\Http\Requests\Bo\Informations\UpdateInformationRequest;
|
||||||
|
use App\Models\Information;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class InformationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $informationModels */
|
||||||
|
$informationModels = Information::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$informationModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'title',
|
||||||
|
'message'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Titre, message');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($informationModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$informationModels = $this->customPaginate($informationModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.informations.index', compact('informationModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Information $information
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Information $information): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.informations.create', [
|
||||||
|
'informationModel' => $information
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Informations\StoreInformationRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreInformationRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$information = new Information();
|
||||||
|
$information->fill($request->validated());
|
||||||
|
|
||||||
|
if ($information->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.informations.edit', $information)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Information $information
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Information $information): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.informations.edit', [
|
||||||
|
'informationModel' => $information,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Informations\UpdateInformationRequest $request
|
||||||
|
* @param \App\Models\Information $information
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @ignore phpcs:disable Generic.Files.LineLength.TooLong
|
||||||
|
*/
|
||||||
|
public function update(UpdateInformationRequest $request, Information $information): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
// phpcs:enable
|
||||||
|
return DB::transaction(function () use ($request, $information) {
|
||||||
|
$information->fill($request->validated());
|
||||||
|
if ($information->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.informations.edit', $information)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Information $information
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Information $information): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($information) {
|
||||||
|
if ($information->deleteOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.informations.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.informations'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
227
app/Http/Controllers/Bo/InvoiceController.php
Normal file
227
app/Http/Controllers/Bo/InvoiceController.php
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Enums\Invoices\InvoiceStatusEnum;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Invoices\StoreInvoiceRequest;
|
||||||
|
use App\Http\Requests\Bo\Invoices\UpdateInvoiceRequest;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Package;
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class InvoiceController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
// If no sorting is requested, apply the default orderByDesc.
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $invoiceModels */
|
||||||
|
$invoiceModels = (!request()->query('sort_col') && !request()->query('sort_way'))
|
||||||
|
? Invoice::query()->orderByDesc('created_at')
|
||||||
|
: Invoice::query();
|
||||||
|
|
||||||
|
$status = intval($request->status);
|
||||||
|
|
||||||
|
switch ($status) {
|
||||||
|
case InvoiceStatusEnum::awaiting_signature->value:
|
||||||
|
$invoiceModels->whereNotNull('sent_client_date')->whereNull('signed_date');
|
||||||
|
break;
|
||||||
|
case InvoiceStatusEnum::awaiting_invoicing->value:
|
||||||
|
$invoiceModels->whereNotNull('sent_client_date')->whereNotNull('signed_date')->whereNull('sent_accounting_date');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ($status !== 0) {
|
||||||
|
$invoiceModels->where('status', $status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$invoiceModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'firstname',
|
||||||
|
'lastname',
|
||||||
|
'ref',
|
||||||
|
'site_id',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom, Prénom, Référence, Site');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($invoiceModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$invoiceModels = $this->customPaginate($invoiceModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.invoices.index', compact('invoiceModels', 'search', 'searchFields', 'status'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show model action.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Invoice $invoice
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function show(Invoice $invoice): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.invoices.show', ['invoiceModel' => $invoice]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Invoice $invoice
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Invoice $invoice): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$packageModels = Package::where('published', true)->get();
|
||||||
|
$taskRowModel = Invoice::getTaskStructure();
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
$companyModels = Company::get();
|
||||||
|
|
||||||
|
return view('bo.pages.invoices.create', [
|
||||||
|
'invoiceModel' => $invoice,
|
||||||
|
'taskRowModel' => $taskRowModel,
|
||||||
|
'packageModels' => $packageModels,
|
||||||
|
'siteModels' => $siteModels,
|
||||||
|
'companyModels' => $companyModels,
|
||||||
|
'invoiceStatusArray' => $invoice->getInvoiceStatusArray($invoice),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Invoices\StoreInvoiceRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreInvoiceRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$invoice = new Invoice();
|
||||||
|
$invoice->fill($request->validated());
|
||||||
|
|
||||||
|
if ($invoice->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.invoices.edit', $invoice)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.invoices'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.invoices'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit model action.
|
||||||
|
*
|
||||||
|
* @param Invoice $invoice
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Invoice $invoice): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$packageModels = Package::get();
|
||||||
|
$taskRowModel = Invoice::getTaskStructure();
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
$companyModels = Company::get();
|
||||||
|
|
||||||
|
return view('bo.pages.invoices.edit', [
|
||||||
|
'invoiceModel' => $invoice,
|
||||||
|
'taskRowModel' => $taskRowModel,
|
||||||
|
'packageModels' => $packageModels,
|
||||||
|
'siteModels' => $siteModels,
|
||||||
|
'companyModels' => $companyModels,
|
||||||
|
'invoiceStatusArray' => $invoice->getInvoiceStatusArray($invoice),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Invoices\UpdateInvoiceRequest $request
|
||||||
|
* @param \App\Models\Invoice $invoice
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateInvoiceRequest $request, Invoice $invoice): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $invoice) {
|
||||||
|
$invoice->fill(($invoice->status->value === InvoiceStatusEnum::opened->value) ? $request->validated() : []);
|
||||||
|
if ($invoice->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.invoices.edit', $invoice)
|
||||||
|
->with('success', __(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => __('models.classes.invoices')]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', __(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => __('models.classes.invoices')]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download Invoice.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Invoice $invoice
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
public function download(Invoice $invoice)
|
||||||
|
{
|
||||||
|
if (Gate::allows('isAdmin')) {
|
||||||
|
return $invoice->generateInvoice()
|
||||||
|
->stream(Str::slug(trans('Devis :appname :date - ref :ref', [
|
||||||
|
'appname' => config('app.name'),
|
||||||
|
'date' =>
|
||||||
|
$invoice->created_at !== $invoice->updated_at ?
|
||||||
|
$invoice->updated_at->isoFormat('YYYY-MM-DD') :
|
||||||
|
$invoice->created_at->isoFormat('YYYY-MM-DD'),
|
||||||
|
'ref' => $invoice->ref,
|
||||||
|
])) . '.pdf');
|
||||||
|
}
|
||||||
|
return redirect()->route('bo.invoices.index')
|
||||||
|
->with('error', __('crud.messages.cannot_be_updated', [
|
||||||
|
'model' => __('models.classes.invoices')
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a paginate resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function jsonPaginate(Request $request): \Illuminate\Http\JsonResponse
|
||||||
|
{
|
||||||
|
$model = $request->targetModel;
|
||||||
|
return DB::transaction(function () use ($model) {
|
||||||
|
$models = $model::query()->orderBy('name', 'ASC')->paginate(12);
|
||||||
|
return \response()->json($models ?? 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
157
app/Http/Controllers/Bo/LabelController.php
Normal file
157
app/Http/Controllers/Bo/LabelController.php
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Labels\StoreLabelRequest;
|
||||||
|
use App\Http\Requests\Bo\Labels\UpdateLabelRequest;
|
||||||
|
use App\Models\Label;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class LabelController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $labelModels */
|
||||||
|
$labelModels = Label::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$labelModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($labelModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$labelModels = $this->customPaginate($labelModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.labels.index', compact('labelModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Label $label
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Label $label): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.labels.create', [
|
||||||
|
'labelModel' => $label
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Labels\StoreLabelRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreLabelRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$label = new Label();
|
||||||
|
$label->fill($request->validated());
|
||||||
|
|
||||||
|
if ($label->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.labels.edit', $label)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Label $label
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Label $label): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.labels.edit', [
|
||||||
|
'labelModel' => $label,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Labels\UpdateLabelRequest $request
|
||||||
|
* @param \App\Models\Label $label
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @ignore phpcs:disable Generic.Files.LineLength.TooLong
|
||||||
|
*/
|
||||||
|
public function update(UpdateLabelRequest $request, Label $label): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
// phpcs:enable
|
||||||
|
return DB::transaction(function () use ($request, $label) {
|
||||||
|
$label->fill($request->validated());
|
||||||
|
if ($label->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.labels.edit', $label)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Label $label
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Label $label): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($label) {
|
||||||
|
if ($label->deleteOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.labels.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.labels'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
177
app/Http/Controllers/Bo/PackageController.php
Normal file
177
app/Http/Controllers/Bo/PackageController.php
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Packages\StorePackageRequest;
|
||||||
|
use App\Http\Requests\Bo\Packages\UpdatePackageRequest;
|
||||||
|
use App\Models\Package;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class PackageController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $packageModels */
|
||||||
|
$packageModels = Package::query();
|
||||||
|
|
||||||
|
$status = $request->status;
|
||||||
|
if ($status === null) {
|
||||||
|
$packageModels->where('published', true);
|
||||||
|
} elseif ($status === '0') {
|
||||||
|
$packageModels->where('published', false);
|
||||||
|
} elseif ($status === '1') {
|
||||||
|
$packageModels->where('published', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$packageModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name',
|
||||||
|
'amount'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom, Montant');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($packageModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$packageModels = $this->customPaginate($packageModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.packages.index', compact('packageModels', 'search', 'searchFields', 'status'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Package $package
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Package $package): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.packages.create', [
|
||||||
|
'packageModel' => $package,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Packages\StorePackageRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StorePackageRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$package = new Package();
|
||||||
|
$package->fill($request->validated());
|
||||||
|
|
||||||
|
if ($package->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.packages.edit', $package)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Package $package
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Package $package): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.packages.edit', [
|
||||||
|
'packageModel' => $package,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Packages\UpdatePackageRequest $request
|
||||||
|
* @param \App\Models\Package $package
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdatePackageRequest $request, Package $package): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $package) {
|
||||||
|
$package->fill($request->validated());
|
||||||
|
if ($package->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.packages.edit', $package)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Package $package
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Package $package): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($package) {
|
||||||
|
if ($package->deleteOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.packages.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.packages'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change model published.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Package $package
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function changePublished(Package $package)
|
||||||
|
{
|
||||||
|
$package->update(['published' => !$package->getOriginal('published')]);
|
||||||
|
return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved')));
|
||||||
|
}
|
||||||
|
}
|
||||||
204
app/Http/Controllers/Bo/SiteController.php
Normal file
204
app/Http/Controllers/Bo/SiteController.php
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Sites\StoreSiteRequest;
|
||||||
|
use App\Http\Requests\Bo\Sites\UpdateSiteRequest;
|
||||||
|
use App\Lib\Helpers\ToolboxHelper;
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class SiteController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $siteModels */
|
||||||
|
$siteModels = Site::query()->with(['invoices', 'tickets']);
|
||||||
|
} else {
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $siteModels */
|
||||||
|
$siteModels = Site::whereHas('users', function (\Illuminate\Database\Eloquent\Builder $query) use ($user) {
|
||||||
|
$query->where('id', $user->id)->with(['invoices', 'tickets']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$siteModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Nom');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($siteModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$siteModels = $this->customPaginate($siteModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.sites.index', compact('siteModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show model action.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Site $site
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function show(Site $site): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.sites.show', ['siteModel' => $site]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Site $site
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(Site $site): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$userModels = User::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.sites.create', [
|
||||||
|
'siteModel' => $site,
|
||||||
|
'userModels' => $userModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Sites\StoreSiteRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreSiteRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$site = new Site();
|
||||||
|
$site->fill($request->validated());
|
||||||
|
if ($site->saveOrFail()) {
|
||||||
|
$site->users()->attach(collect($request->validated('users'))->pluck('id'));
|
||||||
|
return redirect()
|
||||||
|
->route('bo.sites.edit', $site)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Site $site
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Site $site): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$userModels = User::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.sites.edit', [
|
||||||
|
'siteModel' => $site,
|
||||||
|
'userModels' => $userModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Sites\UpdateSiteRequest $request
|
||||||
|
* @param \App\Models\Site $site
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateSiteRequest $request, Site $site): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $site) {
|
||||||
|
$site->fill($request->validated());
|
||||||
|
$site->users()->sync(collect($request->validated('users'))->pluck('id'));
|
||||||
|
if ($site->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.sites.edit', $site)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Site $site
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(Site $site): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($site) {
|
||||||
|
if ($site->deleteOrFail()) {
|
||||||
|
$site->users()->detach();
|
||||||
|
return redirect()
|
||||||
|
->route('bo.sites.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.sites'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a paginate resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function jsonPaginate(Request $request): \Illuminate\Http\JsonResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$model = $request->targetModel;
|
||||||
|
$search = $request->search;
|
||||||
|
$models = $model::where("name", "LIKE", "%{$search}%")->orderBy("name", "ASC");
|
||||||
|
|
||||||
|
return ToolboxHelper::jsonPaginate($this->customPaginate($models, $request->pagination));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
933
app/Http/Controllers/Bo/TicketController.php
Normal file
933
app/Http/Controllers/Bo/TicketController.php
Normal file
|
|
@ -0,0 +1,933 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use App\Mail\NewTicket;
|
||||||
|
use App\Models\Message;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Jobs\UpdateIssueLabels;
|
||||||
|
use App\Lib\Helpers\ToolboxHelper;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use App\Events\NotifySubscriptions;
|
||||||
|
use App\Events\TicketStatusChanged;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\LinkTicket;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use App\Enums\Tickets\TicketTypeEnum;
|
||||||
|
use App\Http\Requests\Bo\SortTickets;
|
||||||
|
use App\Http\Requests\Bo\StoreTicket;
|
||||||
|
use App\Http\Requests\Bo\UpdateTicket;
|
||||||
|
use App\Lib\Helpers\FileStorageHelper;
|
||||||
|
use App\Enums\Tickets\TicketStatusEnum;
|
||||||
|
use App\Events\TicketCreated;
|
||||||
|
use App\Http\Requests\Bo\CreateMessage;
|
||||||
|
use App\Http\Requests\Bo\UpdateMessage;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use GrahamCampbell\GitLab\Facades\GitLab;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use App\Http\Requests\Bo\ChangeTicketStatus;
|
||||||
|
use App\Http\Requests\Bo\UpdateChangeOrderRequest;
|
||||||
|
use App\Http\Requests\Bo\AssignTicketToUserRequest;
|
||||||
|
use App\Http\Requests\Bo\UpdateTicketPriorityRequest;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
|
||||||
|
class TicketController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TicketController
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public readonly Dispatcher $dispatcher
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->authorizeResource(Ticket::class, 'ticket');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tickets List
|
||||||
|
*
|
||||||
|
* @param SortTickets $request
|
||||||
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||||
|
*/
|
||||||
|
public function index(SortTickets $request)
|
||||||
|
{
|
||||||
|
// * Handle Ajax
|
||||||
|
if ($request->ajax()) {
|
||||||
|
$tickets = Ticket::with(['user', 'assignedUser']);
|
||||||
|
if ($request->s) {
|
||||||
|
$tickets = $tickets->where('text', 'LIKE', "%$request->s%")
|
||||||
|
->orWhere('id', $request->s)
|
||||||
|
->orWhereHas('user', function ($q) use ($request) {
|
||||||
|
$q->where('name', 'LIKE', "%$request->s%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** @var \Illuminate/Pagination/LengthAwarePaginator $tickets */
|
||||||
|
$tickets = $tickets->paginate();
|
||||||
|
return \response()->json($tickets->getCollection()->map(function (Ticket $ticket) {
|
||||||
|
return [
|
||||||
|
'label' => sprintf(
|
||||||
|
'Ticket %d (%s): %s',
|
||||||
|
$ticket->id,
|
||||||
|
$ticket->user ? $ticket->user->name : trans('Utilisateur supprimé'),
|
||||||
|
Str::limit(\preg_replace('/[[:cntrl:]]+/', '', $ticket->text), 35)
|
||||||
|
),
|
||||||
|
'value' => $ticket->id
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
} //end if
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$tickets = Ticket::with(['site', 'labels']);
|
||||||
|
if (!$user->isAdmin()) {
|
||||||
|
$tickets->whereHas('site', function ($q) use ($user) {
|
||||||
|
$q->whereHas('users', function ($q) use ($user) {
|
||||||
|
$q->whereId($user->id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$tickets->where('is_internal', false);
|
||||||
|
} else {
|
||||||
|
$tickets->has('site');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tickets->where(
|
||||||
|
'status',
|
||||||
|
($request->st == TicketStatusEnum::archived->value) ? '=' : '<>',
|
||||||
|
TicketStatusEnum::archived
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$user->isAdmin() && $user->sites->count() === 1) {
|
||||||
|
$request->merge([
|
||||||
|
'pr' => $user->sites->first()->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tickets = $this->filterTickets($tickets, $request);
|
||||||
|
$tickets = $this->searchTicket($tickets, $request);
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($tickets);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$tickets = $this->customPaginate($tickets, $request->pagination);
|
||||||
|
|
||||||
|
$sites = Site::select('id', 'name')->orderBy('name')->get();
|
||||||
|
$status = Arr::sort(TicketStatusEnum::cases(), function (TicketStatusEnum $value) {
|
||||||
|
return $value->label();
|
||||||
|
});
|
||||||
|
$types = Arr::sort(TicketTypeEnum::cases(), function (TicketTypeEnum $value) {
|
||||||
|
return $value->label();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view('bo.pages.tickets.index', compact('tickets', 'sites', 'status', 'types'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Ticket
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
if ($request->ajax()) {
|
||||||
|
return \response()->json([
|
||||||
|
'label' => $ticket->formatName($ticket),
|
||||||
|
'value' => $ticket->id
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if (!$user->can('update', $ticket)) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ticket->is_internal && !$user->can('changeInternalStatus', $ticket)) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
|
||||||
|
$isSubscribed = (bool)$ticket->subscriptions()->where('user_id', $user->id)->count();
|
||||||
|
return view('bo.pages.tickets.show', [
|
||||||
|
'ticket' => $ticket,
|
||||||
|
'messages' => $messages,
|
||||||
|
'isSubscribed' => $isSubscribed
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show create Form
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
$sites = Site::orderBy('name')->get();
|
||||||
|
} else {
|
||||||
|
$sites = $user->sites;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count($sites)) {
|
||||||
|
return redirect()->route('bo.sites.create')
|
||||||
|
->with('warning', trans('Vous devez tout d\'abord créer un site pour y ajouter un ticket'));
|
||||||
|
}
|
||||||
|
return view('bo.pages.tickets.create', compact('sites'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ticket
|
||||||
|
*
|
||||||
|
* @param StoreTicket $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreTicket $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
/** @var \App\Models\Site */
|
||||||
|
$site = Site::findOrFail($request->site_id);
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$ticket = new Ticket($request->validated());
|
||||||
|
|
||||||
|
/** @var array<\Illuminate\Http\UploadedFile> */
|
||||||
|
$fileTickets = $request->file_tickets ?? [];
|
||||||
|
if (count($fileTickets) > 0) {
|
||||||
|
foreach ($fileTickets as $key => $file) {
|
||||||
|
if ($key === 0) {
|
||||||
|
$ticket->file = FileStorageHelper::storeFile($ticket, $file);
|
||||||
|
} else {
|
||||||
|
$fileNumber = 'file' . $key + 1;
|
||||||
|
$ticket->$fileNumber = FileStorageHelper::storeFile($ticket, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ticket->user()->associate($user);
|
||||||
|
$ticket->site()->associate($site);
|
||||||
|
$ticket->saveOrFail();
|
||||||
|
|
||||||
|
if ($request->labels) {
|
||||||
|
$ticket->labels()->sync($request->labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->subscriptions()->toggle($ticket->id);
|
||||||
|
|
||||||
|
$this->dispatcher->dispatch(new TicketCreated(
|
||||||
|
siteReceivingTicket: $site,
|
||||||
|
ticket: $ticket,
|
||||||
|
byUserName: $user->name
|
||||||
|
));
|
||||||
|
|
||||||
|
return redirect()->route('bo.ticket.show', $ticket)
|
||||||
|
->with('success', trans('Ticket enregistré !'));
|
||||||
|
});
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
report($e);
|
||||||
|
Session::flash('error', trans('Une erreur est survenue'));
|
||||||
|
} //end try
|
||||||
|
|
||||||
|
return \back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show update form
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
|
*/
|
||||||
|
public function edit(Ticket $ticket)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
$sites = Site::orderBy('name')->get();
|
||||||
|
} else {
|
||||||
|
$sites = $user->sites;
|
||||||
|
}
|
||||||
|
return view('bo.pages.tickets.edit', compact('ticket', 'sites'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Model
|
||||||
|
*
|
||||||
|
* @param UpdateTicket $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateTicket $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
$ticket->fill($request->validated());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return DB::transaction(function () use ($request, $ticket) {
|
||||||
|
/** @var \App\Models\Site */
|
||||||
|
$site = Site::findOrFail($request->site_id);
|
||||||
|
|
||||||
|
/** @var null|array<\Illuminate\Http\UploadedFile> */
|
||||||
|
$fileTickets = $request->file_tickets;
|
||||||
|
if (isset($fileTickets) && count($fileTickets) > 0) {
|
||||||
|
foreach ($fileTickets as $key => $file) {
|
||||||
|
if ($key === 0) {
|
||||||
|
$ticket->file = FileStorageHelper::storeFile($ticket, $file);
|
||||||
|
} else {
|
||||||
|
$fileNumber = 'file' . $key + 1;
|
||||||
|
$ticket->$fileNumber = FileStorageHelper::storeFile($ticket, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ticket->labels()->sync($request->labels);
|
||||||
|
|
||||||
|
$ticket->site()->associate($site);
|
||||||
|
$ticket->saveOrFail();
|
||||||
|
|
||||||
|
return redirect()->route('bo.ticket.edit', $ticket->id)
|
||||||
|
->with('success', 'Modifications enregistrées !');
|
||||||
|
});
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
report($e);
|
||||||
|
Session::flash('error', trans('Une erreur est survenue'));
|
||||||
|
} //end try
|
||||||
|
|
||||||
|
return \back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change ticket Status
|
||||||
|
*
|
||||||
|
* @param ChangeTicketStatus $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function changeStatus(ChangeTicketStatus $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
if ($ticket->gitlab_issue) {
|
||||||
|
\dispatch(new UpdateIssueLabels($ticket, $request->status));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ticket->fill($request->validated());
|
||||||
|
$ticket->saveOrFail();
|
||||||
|
|
||||||
|
return back()
|
||||||
|
->with('warning', trans('Le status à été modifié'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new Message to the Ticket
|
||||||
|
*
|
||||||
|
* @param CreateMessage $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function createMessage(CreateMessage $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return DB::transaction(function () use ($request, $ticket) {
|
||||||
|
/** @var \Illuminate\Http\UploadedFile|null */
|
||||||
|
$filesMessage = $request->file_messages;
|
||||||
|
$message = new Message($request->validated());
|
||||||
|
$message->user()->associate(auth()->user());
|
||||||
|
$message->ticket()->associate($ticket);
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
// Subscribe to ticket.
|
||||||
|
if ($request->subscribe) {
|
||||||
|
$user->subscriptions()->syncWithoutDetaching([$ticket->id]);
|
||||||
|
} else {
|
||||||
|
$user->subscriptions()->detach($ticket->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store files if any.
|
||||||
|
if (isset($filesMessage) and $filesMessage) {
|
||||||
|
foreach ($filesMessage as $key => $singleFile) {
|
||||||
|
if ($key === 0) {
|
||||||
|
$message->file = FileStorageHelper::storeFile($message, $singleFile);
|
||||||
|
$message->saveOrFail();
|
||||||
|
} else {
|
||||||
|
$fileNumber = 'file' . $key + 1;
|
||||||
|
$message->$fileNumber = FileStorageHelper::storeFile($message, $singleFile);
|
||||||
|
$message->saveOrFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$message->saveOrFail();
|
||||||
|
|
||||||
|
return back()
|
||||||
|
->with('success', trans('Votre message à été ajouté'));
|
||||||
|
}, 5);
|
||||||
|
} catch (\ErrorException $e) {
|
||||||
|
report($e);
|
||||||
|
Session::flash('error', trans('Une erreur est survenue'));
|
||||||
|
} //end try
|
||||||
|
|
||||||
|
return back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Message
|
||||||
|
*
|
||||||
|
* @param UpdateMessage $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @param Message $message
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||||
|
*/
|
||||||
|
public function updateMessage(UpdateMessage $request, Ticket $ticket, Message $message)
|
||||||
|
{
|
||||||
|
$message->message = $request->message;
|
||||||
|
$message->saveOrFail();
|
||||||
|
|
||||||
|
return back()
|
||||||
|
->with('success', trans('Votre message à bien été modifié'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change ticket status to archived
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function setToArchive(Ticket $ticket)
|
||||||
|
{
|
||||||
|
$ticket->status = TicketStatusEnum::archived;
|
||||||
|
$ticket->saveOrFail();
|
||||||
|
return redirect()->route('bo.ticket.index')
|
||||||
|
->with('warning', trans('Le ticket est archivé'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to ticket
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function subscribe(Ticket $ticket)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$user->subscriptions()->toggle($ticket->id);
|
||||||
|
if ($user->subscriptions()->where('ticket_id', $ticket->id)->exists()) {
|
||||||
|
return back()
|
||||||
|
->with('success', trans('Vous avez été inscrit'));
|
||||||
|
}
|
||||||
|
return back()->with('warning', trans('Vous avez été désinscrit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen ticket
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function reopen(Ticket $ticket)
|
||||||
|
{
|
||||||
|
$message = trans(':ticketName à réouvert le ticket n°:ticketNumber', [
|
||||||
|
'ticketName' => $ticket->user->name,
|
||||||
|
'ticketNumber' => $ticket->id,
|
||||||
|
]);
|
||||||
|
/** @var \App\Enums\Tickets\TicketStatusEnum */
|
||||||
|
$oldStatus = $ticket->status;
|
||||||
|
$ticket->status = TicketStatusEnum::new;
|
||||||
|
$ticket->saveQuietly();
|
||||||
|
event(new TicketStatusChanged($ticket, $oldStatus, null, $message, false, false));
|
||||||
|
event(new NotifySubscriptions($ticket, $message, collect([$ticket->user])));
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$user->subscriptions()->toggle($ticket->id);
|
||||||
|
|
||||||
|
return back()->with('warning', trans('Votre ticket à été réouvert.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign ticket to user ticket
|
||||||
|
*
|
||||||
|
* @param AssignTicketToUserRequest $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function assignTo(AssignTicketToUserRequest $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
/** @var \App\Models\User|null */
|
||||||
|
if ($assignedUser = User::whereId($request->assign)->first()) {
|
||||||
|
$message = trans(':admin a assigné le ticket n°:ticketNumber à :assignedAdmin', [
|
||||||
|
'admin' => $user->name,
|
||||||
|
'ticketNumber' => $ticket->id,
|
||||||
|
'assignedAdmin' => $assignedUser->name,
|
||||||
|
]);
|
||||||
|
$ticket->assignedUser()->associate($assignedUser);
|
||||||
|
} else {
|
||||||
|
$message = trans(
|
||||||
|
':admin a modifié l\'assignation du ticket n°:ticketNumber, il n\'est plus assigné à quelqu\'un.',
|
||||||
|
[
|
||||||
|
'admin' => $user->name,
|
||||||
|
'ticketNumber' => $ticket->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$ticket->assignedUser()->dissociate();
|
||||||
|
}
|
||||||
|
$ticket->saveQuietly();
|
||||||
|
|
||||||
|
|
||||||
|
/** @var \App\Enums\Tickets\TicketStatusEnum */
|
||||||
|
$oldStatus = $ticket->status;
|
||||||
|
// * Notification systeme doit etre privée.
|
||||||
|
event(new TicketStatusChanged($ticket, $oldStatus, null, $message, true, false));
|
||||||
|
event(new NotifySubscriptions($ticket, $message, collect([$ticket->user])));
|
||||||
|
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$user->subscriptions()->toggle($ticket->id);
|
||||||
|
|
||||||
|
if ($assignedUser) {
|
||||||
|
return back()->with('info', trans('Le ticket à été assigné à :assignedUser', [
|
||||||
|
'assignedUser' => $assignedUser->name
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
return back()->with('info', trans('Le ticket n\'est plus assigné à personne'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link two tickets.
|
||||||
|
*
|
||||||
|
* @param LinkTicket $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function linkTicket(LinkTicket $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
$linkedTicket = Ticket::findOrFail($request->validated())->first();
|
||||||
|
|
||||||
|
if ($ticket->site_id !== $linkedTicket->site_id) {
|
||||||
|
return back()->with('error', trans('Le ticket que vous avez renseigné ne fait pas partie du même site.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ticket->linkedTickets()->where('linked_ticket_id', $linkedTicket->id)->exists()) {
|
||||||
|
$ticket->linkedTickets()->attach($linkedTicket->id);
|
||||||
|
$linkedTicket->linkedTickets()->attach($ticket->id);
|
||||||
|
return back()->with('success', trans('Les tickets ont été liés avec succès.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->with('error', trans('Les tickets sont déjà liés ensemble.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink two tickets.
|
||||||
|
*
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @param Ticket $linkedTicket
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function unlinkTicket(Ticket $ticket, Ticket $linkedTicket)
|
||||||
|
{
|
||||||
|
$ticket->linkedTickets()->detach($linkedTicket->id);
|
||||||
|
$linkedTicket->linkedTickets()->detach($ticket->id);
|
||||||
|
|
||||||
|
return back()->with('warning', trans('Le lien entre les tickets à été supprimé.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// * PRIVATES
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Tickets request constructor
|
||||||
|
*
|
||||||
|
* @param Builder $tickets
|
||||||
|
* @param Request $request
|
||||||
|
* @return Builder
|
||||||
|
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||||
|
*/
|
||||||
|
private function filterTickets(Builder $tickets, Request $request): Builder
|
||||||
|
{
|
||||||
|
//phpcs:enable
|
||||||
|
if ($request->rst) {
|
||||||
|
Session::remove('tickets.ass');
|
||||||
|
Session::remove('tickets.st');
|
||||||
|
Session::remove('tickets.pr');
|
||||||
|
Session::remove('tickets.ty');
|
||||||
|
Session::remove('tickets.sort_col');
|
||||||
|
Session::remove('tickets.sort_way');
|
||||||
|
Session::remove('tickets.or');
|
||||||
|
Session::remove('tickets.search');
|
||||||
|
}
|
||||||
|
if ($request->assr) {
|
||||||
|
Session::remove('tickets.ass');
|
||||||
|
}
|
||||||
|
if ($request->prs) {
|
||||||
|
Session::remove('tickets.pr');
|
||||||
|
}
|
||||||
|
if ($request->sts) {
|
||||||
|
Session::remove('tickets.st');
|
||||||
|
}
|
||||||
|
if ($request->tys) {
|
||||||
|
Session::remove('tickets.ty');
|
||||||
|
}
|
||||||
|
if ($request->sort_col) {
|
||||||
|
Session::put('tickets.sort_col', $request->sort_col);
|
||||||
|
}
|
||||||
|
if ($request->sort_way) {
|
||||||
|
Session::put('tickets.sort_way', $request->sort_way);
|
||||||
|
}
|
||||||
|
if ($request->ord) {
|
||||||
|
Session::remove('tickets.or');
|
||||||
|
}
|
||||||
|
if ($request->pr) {
|
||||||
|
Session::put('tickets.pr', $request->pr);
|
||||||
|
}
|
||||||
|
if ($request->st) {
|
||||||
|
Session::put('tickets.st', $request->st);
|
||||||
|
}
|
||||||
|
if ($request->ty) {
|
||||||
|
Session::put('tickets.ty', $request->ty);
|
||||||
|
}
|
||||||
|
if ($request->ass) {
|
||||||
|
Session::put('tickets.ass', $request->ass);
|
||||||
|
}
|
||||||
|
if (Session::has('tickets.or')) {
|
||||||
|
Session::remove('tickets.ass');
|
||||||
|
Session::remove('tickets.st');
|
||||||
|
Session::remove('tickets.ty');
|
||||||
|
Session::remove('tickets.sort_col');
|
||||||
|
Session::remove('tickets.sort_way');
|
||||||
|
Session::remove('tickets.search');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->or && Session::has('tickets.pr')) {
|
||||||
|
Session::put('tickets.or', $request->or);
|
||||||
|
}
|
||||||
|
|
||||||
|
Session::save();
|
||||||
|
|
||||||
|
if (Session::has('tickets.pr')) {
|
||||||
|
$tickets->where('site_id', Session::get('tickets.pr'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Session::has('tickets.ass')) {
|
||||||
|
$tickets->whereHas('assignedUser', function ($q) {
|
||||||
|
$q->whereId(Session::get('tickets.ass'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Session::has('tickets.st')) {
|
||||||
|
$tickets->where('status', Session::get('tickets.st'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Session::has('tickets.ty')) {
|
||||||
|
$tickets->where('type', Session::get('tickets.ty'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Session::has('tickets.or')) {
|
||||||
|
$tickets->orderBy('order', 'ASC');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Session::has('tickets.sort_col') and
|
||||||
|
Session::has('tickets.sort_way') and
|
||||||
|
in_array(Session::get('tickets.sort_col'), [
|
||||||
|
'type',
|
||||||
|
'title',
|
||||||
|
'assigned_to',
|
||||||
|
'date_mep',
|
||||||
|
'created_at',
|
||||||
|
'created_by'
|
||||||
|
])
|
||||||
|
) {
|
||||||
|
$tickets->orderBy(
|
||||||
|
Session::get('tickets.sort_col'),
|
||||||
|
Session::get('tickets.sort_way')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$tickets->orderBy('last_activity', 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search ticket sql request constructor
|
||||||
|
*
|
||||||
|
* @param Builder $tickets
|
||||||
|
* @param Request $request
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
private function searchTicket(Builder $tickets, Request $request): Builder
|
||||||
|
{
|
||||||
|
if ($request->exists('search')) {
|
||||||
|
if (empty($request->search)) {
|
||||||
|
Session::remove('tickets.search');
|
||||||
|
} else {
|
||||||
|
Session::put('tickets.search', $request->search);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Session::has('tickets.search')) {
|
||||||
|
return $tickets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->searchQuery($tickets, Session::get('tickets.search'), function ($q) {
|
||||||
|
$words = explode(' ', Session::get('tickets.search'));
|
||||||
|
if (\is_array($words)) {
|
||||||
|
foreach ($words as $word) {
|
||||||
|
if (!strlen($word)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$s = collect(TicketStatusEnum::cases())->filter(function (TicketStatusEnum $t) use ($word) {
|
||||||
|
return Str::slug($t->label()) === Str::slug($word);
|
||||||
|
})->map(function (TicketStatusEnum $ticket) {
|
||||||
|
return $ticket->value;
|
||||||
|
});
|
||||||
|
$t = collect(TicketTypeEnum::cases())->filter(function (TicketTypeEnum $s) use ($word) {
|
||||||
|
return Str::slug($s->label()) === Str::slug($word);
|
||||||
|
})->map(function (TicketTypeEnum $ticket) {
|
||||||
|
return $ticket->value;
|
||||||
|
});
|
||||||
|
$q->orWhere('id', $word)
|
||||||
|
->orWhereIn('status', $s)
|
||||||
|
->orWhereIn('type', $t)
|
||||||
|
->orWhereHas('user', function ($q) use ($word) {
|
||||||
|
$q->where('name', 'LIKE', "%$word%");
|
||||||
|
});
|
||||||
|
} //end foreach
|
||||||
|
} //end if
|
||||||
|
}, 'text', 'title', 'file', 'file2', 'file3', 'file4', 'file5');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a paginate resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function jsonPaginate(Request $request): \Illuminate\Http\JsonResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$model = $request->targetModel;
|
||||||
|
$search = $request->search;
|
||||||
|
|
||||||
|
// * Handle Ajax
|
||||||
|
$tickets = $model::with(['user', 'assignedUser']);
|
||||||
|
if ($search) {
|
||||||
|
$tickets = $tickets->where('text', 'LIKE', "%$search%")
|
||||||
|
->orWhere('id', $search)
|
||||||
|
->orWhereHas('user', function ($q) use ($search) {
|
||||||
|
$q->where('name', 'LIKE', "%$search%");
|
||||||
|
})
|
||||||
|
->orderBy("id", "ASC");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToolboxHelper::jsonPaginate(
|
||||||
|
$this->customPaginate($tickets, $request->pagination),
|
||||||
|
'formatName'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a gitlab issue from a ticket.
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function createGitlabIssue(Ticket $ticket)
|
||||||
|
{
|
||||||
|
$userGitlabId = auth()->user()->gitlab_id;
|
||||||
|
$gitlabRepoId = $ticket->site->gitlab_project_id;
|
||||||
|
|
||||||
|
if (GitLab::projects()->show($gitlabRepoId)['archived']) {
|
||||||
|
return back()->with('error', \trans("Ce projet GitLab est archivé et ne peut pas être utilisé."));
|
||||||
|
}
|
||||||
|
|
||||||
|
$description = \sprintf("%s<br>", $ticket->text);
|
||||||
|
$cLabel = match ($ticket->type) {
|
||||||
|
TicketTypeEnum::bug => 'c-bug,g-to-dispatch',
|
||||||
|
TicketTypeEnum::evolution,
|
||||||
|
TicketTypeEnum::mailing => 'c-feature',
|
||||||
|
TicketTypeEnum::newsletter => 'c-feature,g-to-estimate',
|
||||||
|
};
|
||||||
|
|
||||||
|
$assigneeId = match ($ticket->type) {
|
||||||
|
TicketTypeEnum::bug => $userGitlabId,
|
||||||
|
TicketTypeEnum::evolution,
|
||||||
|
TicketTypeEnum::mailing,
|
||||||
|
TicketTypeEnum::newsletter => $ticket->site->projectManager?->gitlab_id
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($ticket->getFiles() as $key => $file) {
|
||||||
|
if (!\is_null($file)) {
|
||||||
|
$uploadedFile = GitLab::projects()->uploadFile($gitlabRepoId, $file);
|
||||||
|
$description .= $this->formatGitlabFiles($ticket, $uploadedFile, $key + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'title' => sprintf(
|
||||||
|
'ts-%s : %s',
|
||||||
|
$ticket->id,
|
||||||
|
$ticket->title
|
||||||
|
),
|
||||||
|
'description' => $description,
|
||||||
|
'labels' => \implode(",", [
|
||||||
|
'c-support',
|
||||||
|
$cLabel
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($assigneeId) {
|
||||||
|
$data['assignee_id'] = $assigneeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$issue = GitLab::issues()->create($gitlabRepoId, $data);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\report($e);
|
||||||
|
return back()->with('error', \trans("Une erreur est survenue"));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\report($e);
|
||||||
|
return back()->with('error', \trans("Une erreur est survenue"));
|
||||||
|
} //end try
|
||||||
|
|
||||||
|
$ticket->gitlab_issue = $issue['web_url'];
|
||||||
|
$ticket->save();
|
||||||
|
|
||||||
|
return back()->with(
|
||||||
|
'success',
|
||||||
|
trans("L'issue a été créée sur Gitlab.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format files for Gitlab issues
|
||||||
|
*
|
||||||
|
* @param \App\Models\Ticket $ticket
|
||||||
|
* @param array $uploadedFile
|
||||||
|
* @param integer $fileNb
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function formatGitlabFiles(Ticket $ticket, array $uploadedFile, int $fileNb)
|
||||||
|
{
|
||||||
|
if (Str::endsWith($ticket->file, ['jpg', 'png', 'jpeg', 'pptx', 'docx', 'xslx', 'csv', 'svg'])) {
|
||||||
|
$file = \sprintf(
|
||||||
|
"",
|
||||||
|
$fileNb,
|
||||||
|
$uploadedFile['url']
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$file = \sprintf(
|
||||||
|
'<a href="%s" target="_blank">%s</a>',
|
||||||
|
$uploadedFile['url'],
|
||||||
|
__('Lien vers le fichier'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return \sprintf("<br><br> Fichier %d : <br>%s<br>", $fileNb, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change order.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\UpdateChangeOrderRequest $request
|
||||||
|
* @param mixed ...$params
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au params utilisé.
|
||||||
|
*/
|
||||||
|
public function changeOrder(UpdateChangeOrderRequest $request, mixed ...$params): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
$paramNames = collect(request()->route()->parameterNames);
|
||||||
|
$className = Str::of($paramNames->get($paramNames->count() - 2))->lower()->studly();
|
||||||
|
$className = "\\App\\Models\\{$className}";
|
||||||
|
$model = Arr::get($params, $paramNames->count() - 2);
|
||||||
|
|
||||||
|
if (!class_exists($className)) {
|
||||||
|
throw new \RuntimeException("The {$className} class doesn't exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!\is_object($model)) {
|
||||||
|
throw new \RuntimeException("The second last param isn't an object.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_class($model) !== (string)Str::of($className)->replaceStart('\\', '')) {
|
||||||
|
throw new \RuntimeException("The {$className} doesn't match second last binded param.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Model|null */
|
||||||
|
$model = $className::query()->where($model->getRouteKeyName(), $model->getRouteKey())->first();
|
||||||
|
|
||||||
|
if (!$model) {
|
||||||
|
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($request, $className, $model) {
|
||||||
|
/** @var \Illuminate\Support\Collection $models */
|
||||||
|
$models = $className::query()
|
||||||
|
->orderBy('order', 'ASC')
|
||||||
|
->where('site_id', $model->site_id)
|
||||||
|
->where('status', '!=', TicketStatusEnum::archived)
|
||||||
|
->where('id', '!=', $model->getKey())
|
||||||
|
->where('order', $request->direction ? '<' : '>', $model->order)
|
||||||
|
->get();
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Model|null $tmp */
|
||||||
|
$tmp = $request->direction ? $models->last() : $models->first();
|
||||||
|
|
||||||
|
if (!$tmp) {
|
||||||
|
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$newOrder = $tmp->getOriginal('order');
|
||||||
|
$tmp->update(['order' => $model->getOriginal('order')]);
|
||||||
|
$model->update(['order' => $newOrder]);
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', trans('crud.messages.order_changed'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update ticket priority.
|
||||||
|
*
|
||||||
|
* @param UpdateTicketPriorityRequest $request
|
||||||
|
* @param Ticket $ticket
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function changePriority(UpdateTicketPriorityRequest $request, Ticket $ticket)
|
||||||
|
{
|
||||||
|
$newPriority = $request->validated('priority');
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($ticket, $newPriority) {
|
||||||
|
// Update all tickets who will be moved with this priority update.
|
||||||
|
$request = Ticket::query()
|
||||||
|
->where('site_id', $ticket->site_id)
|
||||||
|
->whereNot('status', TicketStatusEnum::archived)
|
||||||
|
->whereNot('id', $ticket->id)
|
||||||
|
->whereBetween('order', [min($ticket->order, $newPriority), max($ticket->order, $newPriority)]);
|
||||||
|
$ticket->order < $newPriority ? $request->decrement('order', 1) : $request->increment('order', 1);
|
||||||
|
|
||||||
|
// Update the ticket.
|
||||||
|
$ticket->order = $newPriority;
|
||||||
|
if (!$ticket->saveOrFail()) {
|
||||||
|
return back()->with('error', trans('crud.messages.order_not_changed'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', trans('crud.messages.order_changed'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
81
app/Http/Controllers/Bo/TicketTypeController.php
Normal file
81
app/Http/Controllers/Bo/TicketTypeController.php
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\TicketTypes\UpdateTicketTypeRequest;
|
||||||
|
use App\Models\Ticket\TicketType;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class TicketTypeController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $ticketTypeModels */
|
||||||
|
$ticketTypeModels = TicketType::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$ticketTypeModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'type',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Type');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($ticketTypeModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$ticketTypeModels = $this->customPaginate($ticketTypeModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.ticket_types.index', compact('ticketTypeModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param App\Models\Ticket\TicketType $ticketType
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
* @ignore phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||||
|
*/
|
||||||
|
public function edit(TicketType $ticketType): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.ticket_types.edit', ['ticketTypeModel' => $ticketType]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\TicketTypes\UpdateTicketTypeRequest $request
|
||||||
|
* @param \App\Models\Ticket\TicketType $ticketType
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateTicketTypeRequest $request, TicketType $ticketType): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $ticketType) {
|
||||||
|
$ticketType->fill($request->validated());
|
||||||
|
|
||||||
|
if ($ticketType->saveOrFail()) {
|
||||||
|
return redirect()->route('bo.ticket_types.edit', $ticketType->id)
|
||||||
|
->with('success', __('crud.messages.has_been_updated', [
|
||||||
|
'model' => trans_choice('models.classes.ticket_types', 1)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
return redirect()->route('bo.ticket_types.edit', $ticketType->id)
|
||||||
|
->with('error', __('crud.messages.cannot_be_updated', [
|
||||||
|
'model' => trans_choice('models.classes.ticket_types', 1)
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
232
app/Http/Controllers/Bo/UserController.php
Normal file
232
app/Http/Controllers/Bo/UserController.php
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Bo;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Bo\Users\StoreUserRequest;
|
||||||
|
use App\Http\Requests\Bo\Users\UpdateUserRequest;
|
||||||
|
use App\Http\Controllers\Bo\Auth\ForgotPasswordController;
|
||||||
|
use App\Http\Requests\Bo\Users\FavouriteSiteRequest;
|
||||||
|
|
||||||
|
class UserController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function index(Request $request): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Builder $userModels */
|
||||||
|
$userModels = User::query();
|
||||||
|
|
||||||
|
/** @var string $search Search field */
|
||||||
|
$search = $request->search;
|
||||||
|
if ($search) {
|
||||||
|
$this->searchQuery(
|
||||||
|
$userModels,
|
||||||
|
$search,
|
||||||
|
null,
|
||||||
|
'name',
|
||||||
|
'email'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$searchFields = trans('Label, email');
|
||||||
|
|
||||||
|
/** Sort columns with a query */
|
||||||
|
$this->sortQuery($userModels);
|
||||||
|
|
||||||
|
/** Custom pagination */
|
||||||
|
$userModels = $this->customPaginate($userModels, $request->pagination);
|
||||||
|
|
||||||
|
return view('bo.pages.users.index', compact('userModels', 'search', 'searchFields'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\User $user
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function create(User $user): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.users.create', [
|
||||||
|
'userModel' => $user,
|
||||||
|
'siteModels' => $siteModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Users\StoreUserRequest $request
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function store(StoreUserRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request) {
|
||||||
|
$user = User::query()->onlyTrashed()->where('email', $request->validated('email'))->first() ?? new User();
|
||||||
|
$user->fill($request->safe()->except('sites'));
|
||||||
|
$user->deleted_at = null;
|
||||||
|
|
||||||
|
if ($user->saveOrFail()) {
|
||||||
|
$user->sites()->attach(collect($request->validated('sites'))->pluck('id'));
|
||||||
|
return redirect()
|
||||||
|
->route('bo.users.edit', $user)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_created',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*
|
||||||
|
* @param \App\Models\User $user
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function edit(User $user): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
$siteModels = Site::orderBy('name', 'ASC')->get();
|
||||||
|
|
||||||
|
return view('bo.pages.users.edit', [
|
||||||
|
'userModel' => $user,
|
||||||
|
'siteModels' => $siteModels
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request
|
||||||
|
* @param \App\Models\User $user
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(UpdateUserRequest $request, User $user): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($request, $user) {
|
||||||
|
$user->fill($request->safe()->except(['sites']));
|
||||||
|
$user->sites()->sync(collect($request->validated('sites'))->pluck('id'));
|
||||||
|
if ($user->saveOrFail()) {
|
||||||
|
return redirect()
|
||||||
|
->route('bo.users.edit', $user)
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
} //end if
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
'crud.messages.cannot_be_updated',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*
|
||||||
|
* @param \App\Models\User $user
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function destroy(User $user): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($user) {
|
||||||
|
if ($user->deleteOrFail()) {
|
||||||
|
$user->sites()->detach();
|
||||||
|
return redirect()
|
||||||
|
->route('bo.users.index')
|
||||||
|
->with('success', trans(
|
||||||
|
'crud.messages.has_been_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->with('error', trans(
|
||||||
|
':crud.messages.cannot_be_deleted',
|
||||||
|
['model' => Str::singular(trans('models.classes.users'))]
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset form
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\View
|
||||||
|
*/
|
||||||
|
public function settings(): \Illuminate\Contracts\View\View
|
||||||
|
{
|
||||||
|
return view('bo.pages.users.settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Favourite site submit
|
||||||
|
*
|
||||||
|
* @param \App\Http\Requests\Bo\Users\FavouriteSiteRequest $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function favouriteSiteSubmit(FavouriteSiteRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user->favourite_site_id = $request->favourite_site_id;
|
||||||
|
$user->saveOrFail();
|
||||||
|
|
||||||
|
return redirect()->back()->with("success", trans('crud.messages.favourite_site_changed'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\report($e);
|
||||||
|
return redirect()->back()->with('error', trans('crud.messages.favourite_site_not_changed'));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\report($e);
|
||||||
|
return redirect()->back()->with('error', trans('crud.messages.favourite_site_not_changed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password reset submit
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
|
*/
|
||||||
|
public function passwordResetSubmit(): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
$request = new Request(['email' => $user->email]);
|
||||||
|
return app(ForgotPasswordController::class)->sendResetLinkEmail($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get february last day date of the year
|
||||||
|
*
|
||||||
|
* @param integer $year
|
||||||
|
* @return integer 28 or 29
|
||||||
|
*/
|
||||||
|
private function getLastDayFebruary(int $year): int
|
||||||
|
{
|
||||||
|
if ((is_int($year / 4) && !is_int($year / 100)) || is_int($year / 400)) {
|
||||||
|
return 29;
|
||||||
|
}
|
||||||
|
return 28;
|
||||||
|
}
|
||||||
|
}
|
||||||
247
app/Http/Controllers/Controller.php
Normal file
247
app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Enums\Tickets\TicketStatusEnum;
|
||||||
|
use App\Models\Ticket;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests;
|
||||||
|
use DispatchesJobs;
|
||||||
|
use ValidatesRequests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware(function ($request, $next) {
|
||||||
|
$ignore = [
|
||||||
|
// * Support Web
|
||||||
|
9,
|
||||||
|
// * Gen-Game- Web
|
||||||
|
15,
|
||||||
|
// * Store LDC
|
||||||
|
19,
|
||||||
|
// * CRM
|
||||||
|
27,
|
||||||
|
// * LDC - WEB
|
||||||
|
55,
|
||||||
|
];
|
||||||
|
|
||||||
|
$ticketsNew = Ticket::where('status', TicketStatusEnum::new);
|
||||||
|
$ticketsWaiting = Ticket::where('status', TicketStatusEnum::waiting);
|
||||||
|
$ticketsInProgress = Ticket::whereNotIn(
|
||||||
|
'status',
|
||||||
|
[
|
||||||
|
TicketStatusEnum::new,
|
||||||
|
TicketStatusEnum::estimateTodo,
|
||||||
|
TicketStatusEnum::waiting,
|
||||||
|
TicketStatusEnum::production,
|
||||||
|
TicketStatusEnum::archived,
|
||||||
|
TicketStatusEnum::takenInAccount
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @var \App\Models\User|null */
|
||||||
|
$user = Auth::user();
|
||||||
|
if (!is_null($user) and !$user->isAdmin()) {
|
||||||
|
$ticketsNew = $ticketsNew->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||||
|
$ticketsWaiting = $ticketsWaiting->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||||
|
$ticketsInProgress = $ticketsInProgress->where('is_internal', false)->whereNotIn('site_id', $ignore);
|
||||||
|
}
|
||||||
|
|
||||||
|
View::share('ticketsNew', $ticketsNew->count());
|
||||||
|
View::share('ticketsWaiting', $ticketsWaiting->count());
|
||||||
|
View::share('ticketsInProgress', $ticketsInProgress->count());
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Search query on specified fields
|
||||||
|
* splitting search into words or using whole sentence
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
|
||||||
|
* @param string $search The query string.
|
||||||
|
* @param callable $searchQuery The query string.
|
||||||
|
* @param string ...$fields Either a simple array or a string
|
||||||
|
* in case of a string the two fields
|
||||||
|
* would be concatenate to make the search.
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
protected function searchQuery(
|
||||||
|
Builder $query,
|
||||||
|
string $search,
|
||||||
|
callable $searchQuery = null,
|
||||||
|
string ...$fields
|
||||||
|
): Builder {
|
||||||
|
if (count($fields)) {
|
||||||
|
// Search using words.
|
||||||
|
$words = explode(' ', $search);
|
||||||
|
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
|
||||||
|
foreach ($words as $word) {
|
||||||
|
if (!strlen($word)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->searchOnFields($query, $word, $fields);
|
||||||
|
} //end foreach
|
||||||
|
// Search whole using whole sentence.
|
||||||
|
$this->searchOnFields($query, $search, $fields);
|
||||||
|
if ($searchQuery) {
|
||||||
|
$searchQuery($query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} //end if
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build query search on all fields
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param string $search
|
||||||
|
* @param array $fields
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
private function searchOnFields(
|
||||||
|
Builder $query,
|
||||||
|
string $search,
|
||||||
|
array $fields
|
||||||
|
): \Illuminate\Database\Eloquent\Builder {
|
||||||
|
$table = $query->getModel()->getTable();
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$field = str_replace('?', '\?', $field);
|
||||||
|
if (is_array($field)) {
|
||||||
|
$query->orWhereRaw(
|
||||||
|
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
|
||||||
|
return "`$table`.`$q`";
|
||||||
|
})->implode(', \' \', ')),
|
||||||
|
['%' . htmlspecialchars($search) . '%']
|
||||||
|
);
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
// Get models woth enum's label.
|
||||||
|
if (is_array($this->getSearchValue($query, $search, $field))) {
|
||||||
|
foreach ($this->getSearchValue($query, $search, $field) as $array) {
|
||||||
|
$query->orWhereRaw(
|
||||||
|
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
|
||||||
|
['%' . htmlspecialchars($array->value) . '%']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
// Get models with relation's name/label.
|
||||||
|
if (Str::endsWith($field, '_id')) {
|
||||||
|
$query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) {
|
||||||
|
if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
|
||||||
|
$queryRelation->where('name', 'like', '%' . $search . '%');
|
||||||
|
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
|
||||||
|
$queryRelation->where('label', 'like', '%' . $search . '%');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
$query->orWhereRaw(
|
||||||
|
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
|
||||||
|
['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
|
||||||
|
);
|
||||||
|
} //end foreach
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the search value (on string or Enum).
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param string $search
|
||||||
|
* @param string $field
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
protected function getSearchValue(Builder $query, string $search, string $field): string|array
|
||||||
|
{
|
||||||
|
$cast = $query->getModel()->getCasts()[$field] ?? null;
|
||||||
|
if (
|
||||||
|
count($query->getModel()->getCasts()) and // Check if the model has casts.
|
||||||
|
$cast and // Check if there is cast about specific field.
|
||||||
|
enum_exists($cast) // Check if the cast is an enum.
|
||||||
|
) {
|
||||||
|
/** @var \App\Traits\Enums $cast */
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$cases = collect($cast::toArray());
|
||||||
|
$enum = $cases->filter(function ($item) use ($search) {
|
||||||
|
return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
|
||||||
|
});
|
||||||
|
return $enum->toArray();
|
||||||
|
} else {
|
||||||
|
return $search;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort columns with a query
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param array $ignore
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
protected function sortQuery(Builder $query, array $ignore = []): Builder
|
||||||
|
{
|
||||||
|
$rName = request()->route()->getName();
|
||||||
|
$table = $query->getModel()->getTable();
|
||||||
|
if (request()->sort_col) {
|
||||||
|
Session::put("$rName.sort_col", request()->sort_col);
|
||||||
|
Session::put("$rName.sorted", true);
|
||||||
|
}
|
||||||
|
if (request()->sort_way) {
|
||||||
|
Session::put("$rName.sort_way", request()->sort_way);
|
||||||
|
Session::put("$rName.sorted", true);
|
||||||
|
}
|
||||||
|
if (request()->rst) {
|
||||||
|
Session::remove("$rName.sorted");
|
||||||
|
Session::remove("$rName.sort_col");
|
||||||
|
Session::remove("$rName.sort_way");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
request()->sort_col &&
|
||||||
|
request()->sort_way &&
|
||||||
|
Schema::hasColumn($table, request()->sort_col) &&
|
||||||
|
(!isset($ignore) || (isset($ignore) && !in_array(request()->sort_col, $ignore)))
|
||||||
|
) {
|
||||||
|
$query = $query->orderBy("$table." . request()->sort_col, request()->sort_way);
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize pagination with cache or config (default).
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param integer|null $pagination
|
||||||
|
* @return \Illuminate\Pagination\LengthAwarePaginator
|
||||||
|
*/
|
||||||
|
protected function customPaginate(Builder $query, $pagination): LengthAwarePaginator
|
||||||
|
{
|
||||||
|
if ($pagination) {
|
||||||
|
Cache::put('pagination', $pagination);
|
||||||
|
};
|
||||||
|
$query = $query->paginate(
|
||||||
|
(Cache::get('pagination')) ? intval(Cache::get('pagination')) : config('pagination.default')
|
||||||
|
);
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
app/Http/Kernel.php
Normal file
70
app/Http/Kernel.php
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The application's global HTTP middleware stack.
|
||||||
|
*
|
||||||
|
* These middleware are run during every request to your application.
|
||||||
|
*
|
||||||
|
* @var array<int, class-string|string>
|
||||||
|
*/
|
||||||
|
protected $middleware = [
|
||||||
|
\App\Http\Middleware\TrustHosts::class,
|
||||||
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
|
\Illuminate\Http\Middleware\HandleCors::class,
|
||||||
|
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware groups.
|
||||||
|
*
|
||||||
|
* @var array<string, array<int, class-string|string>>
|
||||||
|
*/
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
'htmlsanitize',
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
\App\Http\Middleware\SecureHeaders::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware.
|
||||||
|
*
|
||||||
|
* These middleware may be assigned to groups or used individually.
|
||||||
|
*
|
||||||
|
* @var array<string, class-string|string>
|
||||||
|
*/
|
||||||
|
protected $routeMiddleware = [
|
||||||
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'is-admin' => \App\Http\Middleware\IsAdmin::class,
|
||||||
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
|
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||||
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
|
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
40
app/Http/Middleware/Authenticate.php
Normal file
40
app/Http/Middleware/Authenticate.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
|
class Authenticate extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function redirectTo(Request $request): ?string
|
||||||
|
{
|
||||||
|
$logins = [
|
||||||
|
'backend' => route('login')
|
||||||
|
];
|
||||||
|
if (!$request->expectsJson()) {
|
||||||
|
// * Notify must login
|
||||||
|
Session::flash('warning', trans('Veuillez vous connecter pour continuer'));
|
||||||
|
|
||||||
|
foreach ($request->route()->gatherMiddleware() as $middleware) {
|
||||||
|
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) {
|
||||||
|
$route = $logins[str_replace('auth:', '', $middleware)] ?? null;
|
||||||
|
if ($route) {
|
||||||
|
return $route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return route('login');
|
||||||
|
}
|
||||||
|
abort(401);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/Http/Middleware/EncryptCookies.php
Normal file
15
app/Http/Middleware/EncryptCookies.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||||
|
|
||||||
|
class EncryptCookies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the cookies that should not be encrypted.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [];
|
||||||
|
}
|
||||||
61
app/Http/Middleware/HtmlSanitizer.php
Normal file
61
app/Http/Middleware/HtmlSanitizer.php
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Lib\Helpers\ToolboxHelper;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class HtmlSanitizer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param string|array ...$params
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, \Closure $next, string|array ...$params): Response
|
||||||
|
{
|
||||||
|
$inputs = $request->all();
|
||||||
|
/** @var callable|null */
|
||||||
|
$cleanInput = null;
|
||||||
|
$cleanInput = function (
|
||||||
|
null|string|array &$input,
|
||||||
|
string $name,
|
||||||
|
?string $rootName = null
|
||||||
|
) use (
|
||||||
|
$params,
|
||||||
|
&$cleanInput
|
||||||
|
): void {
|
||||||
|
// * Nested element array
|
||||||
|
if (is_array($input)) {
|
||||||
|
array_walk_recursive($input, $cleanInput, $name);
|
||||||
|
}
|
||||||
|
if (!is_string($input)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (in_array($name, $params) or in_array($rootName ?? $name, $params)) {
|
||||||
|
// ? Is a json input ?
|
||||||
|
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
|
||||||
|
// ! Decode each json field recursive.
|
||||||
|
\array_walk_recursive($isJson, function (string|array &$item) {
|
||||||
|
if (\is_string($item)) {
|
||||||
|
$item = clean($item);
|
||||||
|
$item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$input = clean($input);
|
||||||
|
$input = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($input);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$input = strip_tags($input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\array_walk($inputs, $cleanInput);
|
||||||
|
$request->merge($inputs);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/Http/Middleware/IsAdmin.php
Normal file
26
app/Http/Middleware/IsAdmin.php
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class IsAdmin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User */
|
||||||
|
$user = auth()->user();
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
return redirect()->route('bo.home');
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
15
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||||
|
|
||||||
|
class PreventRequestsDuringMaintenance extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be reachable while maintenance mode is enabled.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [];
|
||||||
|
}
|
||||||
42
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
42
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class RedirectIfAuthenticated
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
* @param string|null ...$guards
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
* @throws \RuntimeException If guard is unhandled.
|
||||||
|
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, \Closure $next, ...$guards): Response
|
||||||
|
{
|
||||||
|
// phpcs:enable phpcs:disable
|
||||||
|
$guards = empty($guards) ? [null] : $guards;
|
||||||
|
|
||||||
|
foreach ($guards as $guard) {
|
||||||
|
if (Auth::guard($guard)->check()) {
|
||||||
|
switch ($guard) {
|
||||||
|
case 'backend':
|
||||||
|
if (!$request->routeIs('bo.*') && !$request->routeIs('login')) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
return \redirect()->route('bo.dashboard');
|
||||||
|
default:
|
||||||
|
throw new \RuntimeException("Unhandled guard `{$guard}` .");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
103
app/Http/Middleware/SecureHeaders.php
Normal file
103
app/Http/Middleware/SecureHeaders.php
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
|
use Illuminate\Support\Facades\Vite;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SecureHeaders
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Enumerate headers which you do not want in your application's responses.
|
||||||
|
* Great starting point would be to go check out @Scott_Helme's:
|
||||||
|
* https://securityheaders.com/
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $unwantedHeaderList = [
|
||||||
|
'X-Powered-By',
|
||||||
|
'Server',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
// phpcs:enable
|
||||||
|
View::share('nonce', $nonce = Vite::useCspNonce());
|
||||||
|
// * For VueJS dev tool to execute
|
||||||
|
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
|
||||||
|
$scheme = config('app.env') === 'local' ? 'http' : 'https';
|
||||||
|
$reportUri = Route::has('cspReportUri') ?
|
||||||
|
\sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
|
||||||
|
$this->removeUnwantedHeaders($this->unwantedHeaderList);
|
||||||
|
$response = $next($request);
|
||||||
|
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||||
|
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||||
|
$response->headers->set('X-XSS-Protection', '1; mode=block');
|
||||||
|
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||||
|
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
|
||||||
|
$cspPolicies = [
|
||||||
|
"default-src 'none';",
|
||||||
|
"manifest-src 'self';",
|
||||||
|
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
|
||||||
|
"connect-src 'self' blob:;",
|
||||||
|
"img-src * data: blob:;",
|
||||||
|
"frame-src 'self' " . implode(' ', [
|
||||||
|
'https://www.google.com/recaptcha/',
|
||||||
|
'https://recaptcha.google.com/recaptcha/',
|
||||||
|
'https://www.google.com/maps/'
|
||||||
|
]) . ';',
|
||||||
|
"font-src 'self' data: https://fonts.gstatic.com;",
|
||||||
|
// ! Using CKEditor will must have unsafe-inline style
|
||||||
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||||
|
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
|
||||||
|
'https://cdnjs.cloudflare.com',
|
||||||
|
// * File Manager
|
||||||
|
'https://fonts.googleapis.com',
|
||||||
|
'https://cdn.jsdelivr.net',
|
||||||
|
'https://use.fontawesome.com',
|
||||||
|
'https://fonts.googleapis.com'
|
||||||
|
]) . ';',
|
||||||
|
// ! If you dont use ckeditor use this
|
||||||
|
// "style-src 'self' 'nonce-$nonce';",
|
||||||
|
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||||
|
"script-src 'self' {$jsDev} " . \implode(' ', [
|
||||||
|
'https: blob:',
|
||||||
|
'https://www.googletagmanager.com',
|
||||||
|
'https://cdnjs.cloudflare.com',
|
||||||
|
"$scheme://www.google-analytics.com",
|
||||||
|
]) . ';'
|
||||||
|
];
|
||||||
|
|
||||||
|
$response->headers->set(
|
||||||
|
'Content-Security-Policy',
|
||||||
|
implode(' ', $cspPolicies) . $reportUri
|
||||||
|
);
|
||||||
|
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove unwanted headers
|
||||||
|
*
|
||||||
|
* @param array $headerList
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function removeUnwantedHeaders(array $headerList)
|
||||||
|
{
|
||||||
|
foreach ($headerList as $header) {
|
||||||
|
header_remove($header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||||
|
|
||||||
|
class TrimStrings extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the attributes that should not be trimmed.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
}
|
||||||
20
app/Http/Middleware/TrustHosts.php
Normal file
20
app/Http/Middleware/TrustHosts.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||||
|
|
||||||
|
class TrustHosts extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the host patterns that should be trusted.
|
||||||
|
*
|
||||||
|
* @return array<int, string|null>
|
||||||
|
*/
|
||||||
|
public function hosts()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$this->allSubdomainsOfApplicationUrl(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TrustProxies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The trusted proxies for this application.
|
||||||
|
*
|
||||||
|
* @var array<int, string>|string|null
|
||||||
|
*/
|
||||||
|
protected $proxies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The headers that should be used to detect proxies.
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $headers =
|
||||||
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||||
|
}
|
||||||
15
app/Http/Middleware/ValidateSignature.php
Normal file
15
app/Http/Middleware/ValidateSignature.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||||
|
|
||||||
|
class ValidateSignature extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the query string parameters that should be ignored.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [];
|
||||||
|
}
|
||||||
18
app/Http/Middleware/VerifyCsrfToken.php
Normal file
18
app/Http/Middleware/VerifyCsrfToken.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||||
|
|
||||||
|
class VerifyCsrfToken extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be excluded from CSRF verification.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'/csp-report',
|
||||||
|
'/brevo-in'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Auth;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles backend forgotten password form validation.
|
|
||||||
*/
|
|
||||||
class ForgotPasswordRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'email' => trans('validation.attributes.email'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Auth;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Events\Lockout;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\RateLimiter;
|
|
||||||
use Illuminate\Validation\Rules\Password;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
use Uwil\LaravelBackend\Requests\Auth\LoginRequest as BackendLoginRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles backend login form validation, authentication and rate limiting.
|
|
||||||
*/
|
|
||||||
class LoginRequest extends BackendLoginRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
|
||||||
'password' => [
|
|
||||||
'required',
|
|
||||||
'max:255',
|
|
||||||
Password::min(8)
|
|
||||||
->letters()
|
|
||||||
->mixedCase()
|
|
||||||
->numbers()
|
|
||||||
->symbols()
|
|
||||||
->uncompromised(),
|
|
||||||
],
|
|
||||||
'remember' => 'boolean',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'email' => trans('validation.attributes.email'),
|
|
||||||
'password' => trans('validation.attributes.password'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to authenticate the request's credentials.
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Validation\ValidationException Wrong email.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authenticate(): void
|
|
||||||
{
|
|
||||||
$this->ensureIsNotRateLimited();
|
|
||||||
|
|
||||||
$user = User::query()
|
|
||||||
->where('email', $this->input('email'))
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (
|
|
||||||
! $user
|
|
||||||
|| ! $user->userPassword
|
|
||||||
|| ! Hash::check($this->input('password'), $user->userPassword->password)
|
|
||||||
) {
|
|
||||||
RateLimiter::hit($this->throttleKey());
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'email' => trans('auth.failed'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Auth::guard('backend')->login($user, $this->boolean('remember'));
|
|
||||||
RateLimiter::clear($this->throttleKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure the login request is not rate limited.
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Validation\ValidationException Wrong email.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function ensureIsNotRateLimited(): void
|
|
||||||
{
|
|
||||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event(new Lockout($this));
|
|
||||||
|
|
||||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'email' => trans('auth.throttle', [
|
|
||||||
'seconds' => $seconds,
|
|
||||||
'minutes' => ceil($seconds / 60),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Auth;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Validation\Rules\Password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles backend resetting password form validation.
|
|
||||||
*/
|
|
||||||
class ResetPasswordRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
$passwordRules = Password::min(8)->letters()->mixedCase()->numbers()->symbols()->uncompromised();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'token' => 'required|string',
|
|
||||||
'email' => 'required|string|email:rfc,strict,dns,spoof,filter',
|
|
||||||
'password' => [
|
|
||||||
'required',
|
|
||||||
'required_with:password_confirmation',
|
|
||||||
'confirmed',
|
|
||||||
'max:255',
|
|
||||||
$passwordRules,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'token' => trans('validation.custom.token'),
|
|
||||||
'email' => trans('validation.attributes.email'),
|
|
||||||
'password' => trans('validation.attributes.password'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Brands;
|
|
||||||
|
|
||||||
use App\Models\Brand;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for creating a new Brand.
|
|
||||||
*/
|
|
||||||
class StoreBrandRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('create', Brand::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'required|string|between:3,255|unique:brands,name',
|
|
||||||
'sites' => 'nullable|array',
|
|
||||||
'sites.*' => 'integer|exists:sites,id|distinct',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => trans('validation.custom.name'),
|
|
||||||
'sites' => trans_choice('models.classes.sites', \INF),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Brands;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for updating an existing Brand.
|
|
||||||
*/
|
|
||||||
class UpdateBrandRequest extends StoreBrandRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('update', $this->route('brand'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
/** @var \App\Models\Brand $brand */
|
|
||||||
$brand = $this->route('brand');
|
|
||||||
|
|
||||||
return array_merge(parent::rules(), [
|
|
||||||
'name' => [
|
|
||||||
'required',
|
|
||||||
'string',
|
|
||||||
'between:3,255',
|
|
||||||
Rule::unique('brands', 'name')->ignore($brand->getRouteKey()),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\InvoiceCompanies;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for updating an existing InvoiceCompany.
|
|
||||||
*/
|
|
||||||
class UpdateInvoiceCompanyRequest extends StoreInvoiceCompanyRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('update', $this->route('invoice_company'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\InvoicePackages;
|
|
||||||
|
|
||||||
use App\Models\InvoicePackage;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for creating a new InvoicePackage.
|
|
||||||
*/
|
|
||||||
class StoreInvoicePackageRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('create', InvoicePackage::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'required|string|between:3,255',
|
|
||||||
'amount' => 'required|numeric|decimal:0,2|between:1,10000',
|
|
||||||
'published' => 'required|boolean',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => trans('validation.custom.name'),
|
|
||||||
'amount' => trans('validation.custom.amount'),
|
|
||||||
'published' => trans('validation.custom.published'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\InvoicePackages;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for updating an existing InvoicePackage.
|
|
||||||
*/
|
|
||||||
class UpdateInvoicePackageRequest extends StoreInvoicePackageRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('update', $this->route('invoice_package'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Notices;
|
|
||||||
|
|
||||||
use App\Enums\NoticeTypeEnum;
|
|
||||||
use App\Models\Notice;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
use Illuminate\Validation\Rules\Enum;
|
|
||||||
use Uwil\LaravelBackend\Rules\WysiwygLength;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for creating a new Notice.
|
|
||||||
*/
|
|
||||||
class StoreNoticeRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('create', Notice::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'title' => 'required|string|between:3,255',
|
|
||||||
'message' => ['required', 'string', new WysiwygLength()],
|
|
||||||
'type' => ['required', 'integer', new Enum(NoticeTypeEnum::class)],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'title' => trans('validation.custom.title'),
|
|
||||||
'message' => trans('validation.custom.message'),
|
|
||||||
'type' => trans('validation.custom.type'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Notices;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for updating an existing Notice.
|
|
||||||
*/
|
|
||||||
class UpdateNoticeRequest extends StoreNoticeRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('update', $this->route('notice'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Sites;
|
|
||||||
|
|
||||||
use App\Models\Site;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for creating a new Site.
|
|
||||||
*/
|
|
||||||
class StoreSiteRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('create', Site::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare the data for validation.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
protected function prepareForValidation(): void
|
|
||||||
{
|
|
||||||
$this->merge([
|
|
||||||
'project_manager_id' => is_array($this->project_manager_id) && count($this->project_manager_id)
|
|
||||||
? intval($this->project_manager_id[0])
|
|
||||||
: $this->project_manager_id,
|
|
||||||
'gitlab_project_id' => is_array($this->gitlab_project_id)
|
|
||||||
? intval($this->gitlab_project_id[0])
|
|
||||||
: $this->gitlab_project_id,
|
|
||||||
'users' => is_array($this->users) ? array_map(intval(...), $this->users) : $this->users,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'required|string|between:3,255|unique:sites,name',
|
|
||||||
'url_re7' => 'nullable|string|url:http,https|unique:sites,url_re7',
|
|
||||||
'url_prod' => 'nullable|string|url:http,https|unique:sites,url_prod',
|
|
||||||
'project_manager_id' => 'required|integer|exists:users,id',
|
|
||||||
'gitlab_project_id' => 'required|integer|exists:gitlab_projects,id|unique:sites,gitlab_project_id',
|
|
||||||
'users' => 'nullable|array',
|
|
||||||
'users.*' => 'integer|exists:users,id|distinct',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get custom attributes for validator errors.
|
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function attributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => trans('validation.custom.name'),
|
|
||||||
'url_re7' => trans('validation.custom.url_re7'),
|
|
||||||
'url_prod' => trans('validation.custom.url_prod'),
|
|
||||||
'project_manager_id' => trans('validation.custom.project_manager_id'),
|
|
||||||
'gitlab_project_id' => trans_choice('models.classes.gitlab-projects', 1),
|
|
||||||
'users' => trans_choice('uwil-backend::models.classes.users', \INF),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Http\Requests\Back\Sites;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Gate;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles authorization and validation for updating an existing Site.
|
|
||||||
*/
|
|
||||||
class UpdateSiteRequest extends StoreSiteRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function authorize(): bool
|
|
||||||
{
|
|
||||||
return Gate::check('update', $this->route('site'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
#[\Override]
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
/** @var \App\Models\Site $site */
|
|
||||||
$site = $this->route('site');
|
|
||||||
|
|
||||||
return array_merge(parent::rules(), [
|
|
||||||
'name' => [
|
|
||||||
'required',
|
|
||||||
'string',
|
|
||||||
'between:3,255',
|
|
||||||
Rule::unique('sites', 'name')->ignore($site->getRouteKey()),
|
|
||||||
],
|
|
||||||
'url_re7' => [
|
|
||||||
'required',
|
|
||||||
'string',
|
|
||||||
'url:http,https',
|
|
||||||
Rule::unique('sites', 'url_re7')->ignore($site->getRouteKey()),
|
|
||||||
],
|
|
||||||
'url_prod' => [
|
|
||||||
'required',
|
|
||||||
'string',
|
|
||||||
'url:http,https',
|
|
||||||
Rule::unique('sites', 'url_prod')->ignore($site->getRouteKey()),
|
|
||||||
],
|
|
||||||
'gitlab_project_id' => [
|
|
||||||
'required',
|
|
||||||
'integer',
|
|
||||||
'exists:gitlab_projects,id',
|
|
||||||
Rule::unique('sites', 'gitlab_project_id')->ignore($site->getRouteKey()),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue