Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6845a6a332 | |||
| 40f8bb40c9 | |||
| c84aa27d2c | |||
| 77b9323615 | |||
| 6bf194b280 | |||
| cdc9c33f4e | |||
| b45e2d3a95 |
@@ -64,7 +64,6 @@ Ajouter dans le fichier .env du frontend
|
|||||||
* [#FER-13] Faire des recherches sur le scanner des bêtes
|
* [#FER-13] Faire des recherches sur le scanner des bêtes
|
||||||
* [#FER-15] Les non-admin ne peuvent plus supprimer de réception/expédition en attente
|
* [#FER-15] Les non-admin ne peuvent plus supprimer de réception/expédition en attente
|
||||||
* [#FER-17] Ecran d'ajout de bovin
|
* [#FER-17] Ecran d'ajout de bovin
|
||||||
* [#FER-18] Mise à jour du tableau d'arrivage
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
79
README.md
79
README.md
@@ -1,63 +1,50 @@
|
|||||||
# Projet Ferme t
|
# Projet Ferme
|
||||||
|
|
||||||
## Installation du projet
|
## Installation du projet
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Pour windows, il faut installer le WSL2, Ubuntu, docker et nvm.
|
Pour windows, il faut installer le WSL2, Ubuntu, docker et nvm.
|
||||||
Il suffit de suivre cette [doc](https://wiki.malio.fr/bookstack/books/environnement-de-dev/chapter/windows)
|
Il suffit de suivre cette [doc](https://wiki.malio.fr/bookstack/books/environnement-de-dev/chapter/windows)
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
Pour linux, il faut installer docker et nvm.
|
Pour linux, il faut installer docker et nvm.
|
||||||
Il suffit de suivre cette [doc](https://wiki.malio.fr/bookstack/books/environnement-de-dev/chapter/linux)
|
Il suffit de suivre cette [doc](https://wiki.malio.fr/bookstack/books/environnement-de-dev/chapter/linux)
|
||||||
|
|
||||||
### Installation du projet
|
### Installation du projet
|
||||||
|
|
||||||
Une fois les prérequis installés, il suffit de cloner le projet et de lancer les commandes suivantes
|
Une fois les prérequis installés, il suffit de cloner le projet et de lancer les commandes suivantes
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make start
|
make start
|
||||||
make install
|
make install
|
||||||
```
|
```
|
||||||
|
|
||||||
Dans le cas ou le `make start` plante à cause du port de la bdd, il faut modifier **POSTGRES_PORT** dans le fichier .env.docker.local, remplacer le par un port disponible.
|
Dans le cas ou le `make start` plante à cause du port de la bdd, il faut modifier **POSTGRES_PORT** dans le fichier .env.docker.local, remplacer le par un port disponible.
|
||||||
|
|
||||||
### Configuration global
|
### Configuration global
|
||||||
|
|
||||||
Pour les variables d'environnement, il faut demander un .env.local pour le backend et un .env pour le frontend à votre collègue.
|
Pour les variables d'environnement, il faut demander un .env.local pour le backend et un .env pour le frontend à votre collègue.
|
||||||
|
|
||||||
Vérifier que dans le .env.local, vous avez :
|
Vérifier que dans le .env.local, vous avez :
|
||||||
|
* APP_SECRET (à généré dans le conteneur avec la commande php -r "echo bin2hex(random_bytes(32));" et doit être différent de celui de votre collègue, puisque utilisé pour signer des tokens)
|
||||||
- APP_SECRET (à généré dans le conteneur avec la commande php -r "echo bin2hex(random_bytes(32));" et doit être différent de celui de votre collègue, puisque utilisé pour signer des tokens)
|
* DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}?serverVersion=16&charset=utf8"
|
||||||
- DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}?serverVersion=16&charset=utf8"
|
* PONT_BASCULE_BYPASS (doit être à true en dev)
|
||||||
- PONT_BASCULE_BYPASS (doit être à true en dev)
|
* PONT_BASCULE_URL
|
||||||
- PONT_BASCULE_URL
|
* JWT_SECRET_KEY (à générer avec la commande php bin/console lexik:jwt:generate-keypair)
|
||||||
- JWT_SECRET_KEY (à générer avec la commande php bin/console lexik:jwt:generate-keypair)
|
* JWT_PUBLIC_KEY
|
||||||
- JWT_PUBLIC_KEY
|
* JWT_PASSPHRASE (à généré dans le conteneur avec la commande php -r "echo bin2hex(random_bytes(32));")
|
||||||
- JWT_PASSPHRASE (à généré dans le conteneur avec la commande php -r "echo bin2hex(random_bytes(32));")
|
* COOKIE_SECURE=0 (en dev 0 et en prod 1. Si c'est du http, laisser en 0)
|
||||||
- COOKIE_SECURE=0 (en dev 0 et en prod 1. Si c'est du http, laisser en 0)
|
|
||||||
|
|
||||||
Vérifier que dans le .env du dossier frontend, vous avez :
|
Vérifier que dans le .env du dossier frontend, vous avez :
|
||||||
|
* NUXT_PUBLIC_API_BASE="http://localhost:8080/api"
|
||||||
- NUXT_PUBLIC_API_BASE="http://localhost:8080/api"
|
|
||||||
|
|
||||||
### Configuration xdebug
|
### Configuration xdebug
|
||||||
|
|
||||||
Pour configurer xdebug, il faut ajouter un serveur sur phpstorm. <br>
|
Pour configurer xdebug, il faut ajouter un serveur sur phpstorm. <br>
|
||||||
Pour cela, il faut aller dans **Settings > PHP > Servers** <br>
|
Pour cela, il faut aller dans **Settings > PHP > Servers** <br>
|
||||||
|
* Name : ferme-docker
|
||||||
- Name : ferme-docker
|
* Host : localhost
|
||||||
- Host : localhost
|
* Port : 8080
|
||||||
- Port : 8080
|
* Path : File/Directory -> l'endroit où est stocké votre projet et le path -> /var/www/html
|
||||||
- Path : File/Directory -> l'endroit où est stocké votre projet et le path -> /var/www/html
|
|
||||||
|
|
||||||
Pour que xdebug fonctionne sur windows, il faut modifier la variable **XDEBUG_CLIENT_HOST** par votre ip local
|
Pour que xdebug fonctionne sur windows, il faut modifier la variable **XDEBUG_CLIENT_HOST** par votre ip local
|
||||||
|
|
||||||
## Utilisation du projet
|
## Utilisation du projet
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
|
||||||
L'api est disponible sur http://localhost:8080/api
|
L'api est disponible sur http://localhost:8080/api
|
||||||
Pour la bdd toutes les infos sont dans le fichier **docker/.env.docker.local**
|
Pour la bdd toutes les infos sont dans le fichier **docker/.env.docker.local**
|
||||||
Vous pouvez modifier le port si nécessaire.
|
Vous pouvez modifier le port si nécessaire.
|
||||||
@@ -66,22 +53,17 @@ La bdd est déja pré-configuré dans PhpStorm, il suffit de rentrer les infos d
|
|||||||
C'est un bdd local dans le docker.
|
C'est un bdd local dans le docker.
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
Pour le frontend, il suffit de taper la commande suivante qui va lancer le serveur de dev
|
Pour le frontend, il suffit de taper la commande suivante qui va lancer le serveur de dev
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make dev-nuxt
|
make dev-nuxt
|
||||||
```
|
```
|
||||||
|
|
||||||
Le front sera accessible sur http://localhost:3000
|
Le front sera accessible sur http://localhost:3000
|
||||||
|
|
||||||
### Authentification
|
### Authentification
|
||||||
|
|
||||||
Ce projet utilise l'authentification JWT avec un cookie httpOnly (LexikJWTAuthenticationBundle).
|
Ce projet utilise l'authentification JWT avec un cookie httpOnly (LexikJWTAuthenticationBundle).
|
||||||
Le frontend ne lit jamais directement le token, le navigateur envoie automatiquement le cookie.
|
Le frontend ne lit jamais directement le token, le navigateur envoie automatiquement le cookie.
|
||||||
|
|
||||||
### Login flow
|
### Login flow
|
||||||
|
|
||||||
- Frontend envoie les identifiants à:
|
- Frontend envoie les identifiants à:
|
||||||
- `POST /api/login_check`
|
- `POST /api/login_check`
|
||||||
- Backend returns:
|
- Backend returns:
|
||||||
@@ -91,26 +73,20 @@ Le frontend ne lit jamais directement le token, le navigateur envoie automatique
|
|||||||
- La déconnexion utilise `POST /api/logout` et redirige vers `/login`.
|
- La déconnexion utilise `POST /api/logout` et redirige vers `/login`.
|
||||||
|
|
||||||
### Fixtures
|
### Fixtures
|
||||||
|
|
||||||
Pour lancer les fixtures (Attention sa purge la bdd complètement)
|
Pour lancer les fixtures (Attention sa purge la bdd complètement)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php bin/console doctrine:fixtures:load
|
php bin/console doctrine:fixtures:load
|
||||||
```
|
```
|
||||||
|
|
||||||
Attention cette commande est dangereuse, à utiliser que pour les débuts de la prod ou en recette.
|
Attention cette commande est dangereuse, à utiliser que pour les débuts de la prod ou en recette.
|
||||||
Dans un premier temps pour remplir les listes, vous pouvez lancer la commande symfony
|
Dans un premier temps pour remplir les listes, vous pouvez lancer la commande symfony
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
php bin/console app:seed
|
php bin/console app:seed
|
||||||
```
|
```
|
||||||
|
|
||||||
La commande va faire une update ou une création en fonction des data existante.
|
La commande va faire une update ou une création en fonction des data existante.
|
||||||
|
|
||||||
## Livraison en recette
|
## Livraison en recette
|
||||||
|
|
||||||
### Préparatifs
|
### Préparatifs
|
||||||
|
|
||||||
Avant de déployer, il faut penser à ajouter les variables d'env s'il y a des changements/modifications.
|
Avant de déployer, il faut penser à ajouter les variables d'env s'il y a des changements/modifications.
|
||||||
Le .env se trouve /var/www/ferme/.env
|
Le .env se trouve /var/www/ferme/.env
|
||||||
|
|
||||||
@@ -119,62 +95,43 @@ Sur la machine, il est disponible dans /usr/local/bin/deploy-ferme <br>
|
|||||||
Pour le modifier, il faut copier le contenu du deploy-release.sh dans le deploy-ferme
|
Pour le modifier, il faut copier le contenu du deploy-release.sh dans le deploy-ferme
|
||||||
|
|
||||||
### Livraison
|
### Livraison
|
||||||
|
|
||||||
Sur le serveur de recette, il suffit d'utiliser cette commande pour livrer
|
Sur le serveur de recette, il suffit d'utiliser cette commande pour livrer
|
||||||
|
```bash
|
||||||
```bash
|
|
||||||
/usr/local/bin/deploy-ferme vX.Y.Z
|
/usr/local/bin/deploy-ferme vX.Y.Z
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commandes utiles
|
## Commandes utiles
|
||||||
|
|
||||||
Pour restart le container
|
Pour restart le container
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make restart
|
make restart
|
||||||
```
|
```
|
||||||
|
|
||||||
Pour lancer les TU
|
Pour lancer les TU
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make test
|
make test
|
||||||
```
|
```
|
||||||
|
|
||||||
Pour accéder au container et lance des commandes
|
Pour accéder au container et lance des commandes
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make shell
|
make shell
|
||||||
```
|
```
|
||||||
|
|
||||||
Pour clear le cache Symfony
|
Pour clear le cache Symfony
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make cache-clear
|
make cache-clear
|
||||||
```
|
```
|
||||||
|
|
||||||
Faire une migration
|
Faire une migration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make migration-migrate
|
make migration-migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
Pour générer un password pour un user
|
Pour générer un password pour un user
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make shell
|
make shell
|
||||||
php bin/console security:hash-password
|
php bin/console security:hash-password
|
||||||
```
|
```
|
||||||
|
|
||||||
Sélectionner entity User, taper sont mdp, le copier et l'ajouter dans l'insert de bdd suivant :
|
Sélectionner entity User, taper sont mdp, le copier et l'ajouter dans l'insert de bdd suivant :
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
INSERT INTO "user" (username, roles, password)
|
INSERT INTO "user" (username, roles, password)
|
||||||
VALUES ('Mon user', '["ROLE_USER"]', 'Mon mdp hashé');
|
VALUES ('Mon user', '["ROLE_USER"]', 'Mon mdp hashé');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Gestion des logs
|
## Gestion des logs
|
||||||
|
|
||||||
Pour suivre les logs en temps réel :
|
Pour suivre les logs en temps réel :
|
||||||
|
* tail -f var/log/dev.log
|
||||||
- tail -f var/log/dev.log
|
* tail -f var/log/prod.log
|
||||||
- tail -f var/log/prod.log
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"doctrine/orm": "^3.6",
|
"doctrine/orm": "^3.6",
|
||||||
"dompdf/dompdf": "^3.1",
|
"dompdf/dompdf": "^3.1",
|
||||||
"lexik/jwt-authentication-bundle": "*",
|
"lexik/jwt-authentication-bundle": "*",
|
||||||
"malio/ednotif-bundle": ">=0.0.6",
|
"malio/ednotif-bundle": ">=0.0.4",
|
||||||
"nelmio/cors-bundle": "^2.6",
|
"nelmio/cors-bundle": "^2.6",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6",
|
"phpdocumentor/reflection-docblock": "^5.6",
|
||||||
"phpstan/phpdoc-parser": "^2.3",
|
"phpstan/phpdoc-parser": "^2.3",
|
||||||
|
|||||||
176
composer.lock
generated
176
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "fd62fc3833815b11aa058fd2759c1c79",
|
"content-hash": "9c04091eea0e10c19713a1d882b04f91",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "api-platform/doctrine-common",
|
"name": "api-platform/doctrine-common",
|
||||||
@@ -2706,11 +2706,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "malio/ednotif-bundle",
|
"name": "malio/ednotif-bundle",
|
||||||
"version": "v0.0.6",
|
"version": "v0.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitea.malio.fr/MALIO-DEV/ednotif-bundle",
|
"url": "https://gitea.malio.fr/MALIO-DEV/ednotif-bundle",
|
||||||
"reference": "f757822f366bd5f55588aa89e0ec5a5d0e811f1f"
|
"reference": "92c058213b34ba61f4aa6c03e11ce1ea8cc71421"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-soap": "*",
|
"ext-soap": "*",
|
||||||
@@ -2744,7 +2744,7 @@
|
|||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"description": "Client EDNOTIF (Guichet + wsIpBNotif) pour Symfony",
|
"description": "Client EDNOTIF (Guichet + wsIpBNotif) pour Symfony",
|
||||||
"time": "2026-04-21T08:14:37+00:00"
|
"time": "2026-01-26T13:24:38+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "masterminds/html5",
|
"name": "masterminds/html5",
|
||||||
@@ -3655,16 +3655,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache",
|
"name": "symfony/cache",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/cache.git",
|
"url": "https://github.com/symfony/cache.git",
|
||||||
"reference": "8abf3ccbeae9d3071b81a3ae7ee11b209f9e1e78"
|
"reference": "5d3fcada5e1b80157cfdfd1f9dbbd63f95ef6f13"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/cache/zipball/8abf3ccbeae9d3071b81a3ae7ee11b209f9e1e78",
|
"url": "https://api.github.com/repos/symfony/cache/zipball/5d3fcada5e1b80157cfdfd1f9dbbd63f95ef6f13",
|
||||||
"reference": "8abf3ccbeae9d3071b81a3ae7ee11b209f9e1e78",
|
"reference": "5d3fcada5e1b80157cfdfd1f9dbbd63f95ef6f13",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3731,7 +3731,7 @@
|
|||||||
"psr6"
|
"psr6"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/cache/tree/v8.0.8"
|
"source": "https://github.com/symfony/cache/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3751,7 +3751,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:18:51+00:00"
|
"time": "2026-01-23T12:59:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache-contracts",
|
"name": "symfony/cache-contracts",
|
||||||
@@ -3908,16 +3908,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/config",
|
"name": "symfony/config",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/config.git",
|
"url": "https://github.com/symfony/config.git",
|
||||||
"reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39"
|
"reference": "8f45af92f08f82902827a8b6f403aaf49d893539"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/config/zipball/c7369cc1da250fcbfe0c5a9d109e419661549c39",
|
"url": "https://api.github.com/repos/symfony/config/zipball/8f45af92f08f82902827a8b6f403aaf49d893539",
|
||||||
"reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39",
|
"reference": "8f45af92f08f82902827a8b6f403aaf49d893539",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3962,7 +3962,7 @@
|
|||||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/config/tree/v8.0.8"
|
"source": "https://github.com/symfony/config/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3982,7 +3982,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2026-01-13T13:06:50+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
@@ -4076,16 +4076,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/dependency-injection",
|
"name": "symfony/dependency-injection",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/dependency-injection.git",
|
"url": "https://github.com/symfony/dependency-injection.git",
|
||||||
"reference": "3ce58b0fa844dc647ca1d66ea34748af985728c5"
|
"reference": "59c3cf87a7ed9beb561cf7433a6635b000a0ff4d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/3ce58b0fa844dc647ca1d66ea34748af985728c5",
|
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/59c3cf87a7ed9beb561cf7433a6635b000a0ff4d",
|
||||||
"reference": "3ce58b0fa844dc647ca1d66ea34748af985728c5",
|
"reference": "59c3cf87a7ed9beb561cf7433a6635b000a0ff4d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4133,7 +4133,7 @@
|
|||||||
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/dependency-injection/tree/v8.0.8"
|
"source": "https://github.com/symfony/dependency-injection/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4153,7 +4153,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-31T07:15:36+00:00"
|
"time": "2026-01-23T12:59:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
@@ -4400,16 +4400,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/error-handler",
|
"name": "symfony/error-handler",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/error-handler.git",
|
"url": "https://github.com/symfony/error-handler.git",
|
||||||
"reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517"
|
"reference": "7620b97ec0ab1d2d6c7fb737aa55da411bea776a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
|
"url": "https://api.github.com/repos/symfony/error-handler/zipball/7620b97ec0ab1d2d6c7fb737aa55da411bea776a",
|
||||||
"reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517",
|
"reference": "7620b97ec0ab1d2d6c7fb737aa55da411bea776a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4457,7 +4457,7 @@
|
|||||||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/error-handler/tree/v8.0.8"
|
"source": "https://github.com/symfony/error-handler/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4477,20 +4477,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2026-01-23T11:07:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||||
"reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6"
|
"reference": "99301401da182b6cfaa4700dbe9987bb75474b47"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6",
|
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47",
|
||||||
"reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6",
|
"reference": "99301401da182b6cfaa4700dbe9987bb75474b47",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4542,7 +4542,7 @@
|
|||||||
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8"
|
"source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4562,7 +4562,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2026-01-05T11:45:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher-contracts",
|
"name": "symfony/event-dispatcher-contracts",
|
||||||
@@ -4709,16 +4709,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/filesystem",
|
"name": "symfony/filesystem",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/filesystem.git",
|
"url": "https://github.com/symfony/filesystem.git",
|
||||||
"reference": "66b769ae743ce2d13e435528fbef4af03d623e5a"
|
"reference": "d937d400b980523dc9ee946bb69972b5e619058d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a",
|
"url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d",
|
||||||
"reference": "66b769ae743ce2d13e435528fbef4af03d623e5a",
|
"reference": "d937d400b980523dc9ee946bb69972b5e619058d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4755,7 +4755,7 @@
|
|||||||
"description": "Provides basic utilities for the filesystem",
|
"description": "Provides basic utilities for the filesystem",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/filesystem/tree/v8.0.8"
|
"source": "https://github.com/symfony/filesystem/tree/v8.0.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4775,7 +4775,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2025-12-01T09:13:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/finder",
|
"name": "symfony/finder",
|
||||||
@@ -5234,16 +5234,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-foundation",
|
"name": "symfony/http-foundation",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-foundation.git",
|
"url": "https://github.com/symfony/http-foundation.git",
|
||||||
"reference": "02656f7ebeae5c155d659e946f6b3a33df24051b"
|
"reference": "e33ba71e674a1bb16eb251688bd27c2ff67e0dc1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b",
|
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/e33ba71e674a1bb16eb251688bd27c2ff67e0dc1",
|
||||||
"reference": "02656f7ebeae5c155d659e946f6b3a33df24051b",
|
"reference": "e33ba71e674a1bb16eb251688bd27c2ff67e0dc1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5290,7 +5290,7 @@
|
|||||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-foundation/tree/v8.0.8"
|
"source": "https://github.com/symfony/http-foundation/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5310,20 +5310,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2026-01-09T12:15:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-kernel",
|
"name": "symfony/http-kernel",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-kernel.git",
|
"url": "https://github.com/symfony/http-kernel.git",
|
||||||
"reference": "1770f6818d83b2fddc12185025b93f39a90cb628"
|
"reference": "3e61425b663a995957217d03c444b9d27ca7d928"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/1770f6818d83b2fddc12185025b93f39a90cb628",
|
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/3e61425b663a995957217d03c444b9d27ca7d928",
|
||||||
"reference": "1770f6818d83b2fddc12185025b93f39a90cb628",
|
"reference": "3e61425b663a995957217d03c444b9d27ca7d928",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5394,7 +5394,7 @@
|
|||||||
"description": "Provides a structured process for converting a Request into a Response",
|
"description": "Provides a structured process for converting a Request into a Response",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-kernel/tree/v8.0.8"
|
"source": "https://github.com/symfony/http-kernel/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5414,7 +5414,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-31T21:14:05+00:00"
|
"time": "2026-01-25T08:21:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/monolog-bridge",
|
"name": "symfony/monolog-bridge",
|
||||||
@@ -5574,16 +5574,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/options-resolver",
|
"name": "symfony/options-resolver",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/options-resolver.git",
|
"url": "https://github.com/symfony/options-resolver.git",
|
||||||
"reference": "b48bce0a70b914f6953dafbd10474df232ed4de8"
|
"reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8",
|
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7",
|
||||||
"reference": "b48bce0a70b914f6953dafbd10474df232ed4de8",
|
"reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5621,7 +5621,7 @@
|
|||||||
"options"
|
"options"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/options-resolver/tree/v8.0.8"
|
"source": "https://github.com/symfony/options-resolver/tree/v8.0.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5641,7 +5641,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2025-11-12T15:55:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/password-hasher",
|
"name": "symfony/password-hasher",
|
||||||
@@ -5885,16 +5885,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.36.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315"
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
"reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315",
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5946,7 +5946,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.36.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5966,20 +5966,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-04-10T17:25:58+00:00"
|
"time": "2024-12-23T08:48:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php85",
|
"name": "symfony/polyfill-php85",
|
||||||
"version": "v1.36.0",
|
"version": "v1.33.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php85.git",
|
"url": "https://github.com/symfony/polyfill-php85.git",
|
||||||
"reference": "2c408a6bb0313e6001a83628dc5506100474254e"
|
"reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/2c408a6bb0313e6001a83628dc5506100474254e",
|
"url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
|
||||||
"reference": "2c408a6bb0313e6001a83628dc5506100474254e",
|
"reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6026,7 +6026,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php85/tree/v1.36.0"
|
"source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6046,7 +6046,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-04-10T16:50:15+00:00"
|
"time": "2025-06-23T16:12:55+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-uuid",
|
"name": "symfony/polyfill-uuid",
|
||||||
@@ -7667,16 +7667,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/var-dumper",
|
"name": "symfony/var-dumper",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/var-dumper.git",
|
"url": "https://github.com/symfony/var-dumper.git",
|
||||||
"reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1"
|
"reference": "326e0406fc315eca57ef5740fa4a280b7a068c82"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
|
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82",
|
||||||
"reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1",
|
"reference": "326e0406fc315eca57ef5740fa4a280b7a068c82",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -7730,7 +7730,7 @@
|
|||||||
"dump"
|
"dump"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/var-dumper/tree/v8.0.8"
|
"source": "https://github.com/symfony/var-dumper/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -7750,20 +7750,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-31T07:15:36+00:00"
|
"time": "2026-01-01T23:07:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/var-exporter",
|
"name": "symfony/var-exporter",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/var-exporter.git",
|
"url": "https://github.com/symfony/var-exporter.git",
|
||||||
"reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6"
|
"reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/15776bb07a91b089037da89f8832fa41d5fa6ec6",
|
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04",
|
||||||
"reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6",
|
"reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -7810,7 +7810,7 @@
|
|||||||
"serialize"
|
"serialize"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/var-exporter/tree/v8.0.8"
|
"source": "https://github.com/symfony/var-exporter/tree/v8.0.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -7830,7 +7830,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2025-11-05T18:53:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/web-link",
|
"name": "symfony/web-link",
|
||||||
@@ -11457,16 +11457,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/process",
|
"name": "symfony/process",
|
||||||
"version": "v8.0.8",
|
"version": "v8.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/process.git",
|
"url": "https://github.com/symfony/process.git",
|
||||||
"reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc"
|
"reference": "10df72602d88c0a3fa685b822976a052611dd607"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
|
"url": "https://api.github.com/repos/symfony/process/zipball/10df72602d88c0a3fa685b822976a052611dd607",
|
||||||
"reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc",
|
"reference": "10df72602d88c0a3fa685b822976a052611dd607",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11498,7 +11498,7 @@
|
|||||||
"description": "Executes commands in sub-processes",
|
"description": "Executes commands in sub-processes",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/process/tree/v8.0.8"
|
"source": "https://github.com/symfony/process/tree/v8.0.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11518,7 +11518,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-03-30T15:14:47+00:00"
|
"time": "2026-01-23T11:07:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/web-profiler-bundle",
|
"name": "symfony/web-profiler-bundle",
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ api_platform:
|
|||||||
stateless: true
|
stateless: true
|
||||||
cache_headers:
|
cache_headers:
|
||||||
vary: ['Content-Type', 'Authorization', 'Origin']
|
vary: ['Content-Type', 'Authorization', 'Origin']
|
||||||
pagination_client_items_per_page: true
|
|
||||||
pagination_maximum_items_per_page: 100
|
|
||||||
formats:
|
formats:
|
||||||
json: ['application/json']
|
json: ['application/json']
|
||||||
jsonld: ['application/ld+json']
|
jsonld: ['application/ld+json']
|
||||||
|
|||||||
@@ -210,18 +210,18 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* initial_marking?: list<scalar|Param|null>,
|
* initial_marking?: list<scalar|Param|null>,
|
||||||
* events_to_dispatch?: list<string|Param>|null,
|
* events_to_dispatch?: list<string|Param>|null,
|
||||||
* places?: list<array{ // Default: []
|
* places?: list<array{ // Default: []
|
||||||
* name?: scalar|Param|null,
|
* name: scalar|Param|null,
|
||||||
* metadata?: list<mixed>,
|
* metadata?: list<mixed>,
|
||||||
* }>,
|
* }>,
|
||||||
* transitions?: list<array{ // Default: []
|
* transitions: list<array{ // Default: []
|
||||||
* name?: string|Param,
|
* name: string|Param,
|
||||||
* guard?: string|Param, // An expression to block the transition.
|
* guard?: string|Param, // An expression to block the transition.
|
||||||
* from?: list<array{ // Default: []
|
* from?: list<array{ // Default: []
|
||||||
* place?: string|Param,
|
* place: string|Param,
|
||||||
* weight?: int|Param, // Default: 1
|
* weight?: int|Param, // Default: 1
|
||||||
* }>,
|
* }>,
|
||||||
* to?: list<array{ // Default: []
|
* to?: list<array{ // Default: []
|
||||||
* place?: string|Param,
|
* place: string|Param,
|
||||||
* weight?: int|Param, // Default: 1
|
* weight?: int|Param, // Default: 1
|
||||||
* }>,
|
* }>,
|
||||||
* weight?: int|Param, // Default: 1
|
* weight?: int|Param, // Default: 1
|
||||||
@@ -232,7 +232,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* },
|
* },
|
||||||
* router?: bool|array{ // Router configuration
|
* router?: bool|array{ // Router configuration
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* resource?: scalar|Param|null,
|
* resource: scalar|Param|null,
|
||||||
* type?: scalar|Param|null,
|
* type?: scalar|Param|null,
|
||||||
* default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null
|
* default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null
|
||||||
* http_port?: scalar|Param|null, // Default: 80
|
* http_port?: scalar|Param|null, // Default: 80
|
||||||
@@ -457,7 +457,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* allow_no_senders?: bool|Param, // Default: true
|
* allow_no_senders?: bool|Param, // Default: true
|
||||||
* },
|
* },
|
||||||
* middleware?: list<string|array{ // Default: []
|
* middleware?: list<string|array{ // Default: []
|
||||||
* id?: scalar|Param|null,
|
* id: scalar|Param|null,
|
||||||
* arguments?: list<mixed>,
|
* arguments?: list<mixed>,
|
||||||
* }>,
|
* }>,
|
||||||
* }>,
|
* }>,
|
||||||
@@ -629,7 +629,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* lock_factory?: scalar|Param|null, // The service ID of the lock factory used by this limiter (or null to disable locking). // Default: "auto"
|
* lock_factory?: scalar|Param|null, // The service ID of the lock factory used by this limiter (or null to disable locking). // Default: "auto"
|
||||||
* cache_pool?: scalar|Param|null, // The cache pool to use for storing the current limiter state. // Default: "cache.rate_limiter"
|
* cache_pool?: scalar|Param|null, // The cache pool to use for storing the current limiter state. // Default: "cache.rate_limiter"
|
||||||
* storage_service?: scalar|Param|null, // The service ID of a custom storage implementation, this precedes any configured "cache_pool". // Default: null
|
* storage_service?: scalar|Param|null, // The service ID of a custom storage implementation, this precedes any configured "cache_pool". // Default: null
|
||||||
* policy?: "fixed_window"|"token_bucket"|"sliding_window"|"compound"|"no_limit"|Param, // The algorithm to be used by this limiter.
|
* policy: "fixed_window"|"token_bucket"|"sliding_window"|"compound"|"no_limit"|Param, // The algorithm to be used by this limiter.
|
||||||
* limiters?: list<scalar|Param|null>,
|
* limiters?: list<scalar|Param|null>,
|
||||||
* limit?: int|Param, // The maximum allowed hits in a fixed interval or burst.
|
* limit?: int|Param, // The maximum allowed hits in a fixed interval or burst.
|
||||||
* interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
|
* interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent).
|
||||||
@@ -674,7 +674,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus"
|
* message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus"
|
||||||
* routing?: array<string, array{ // Default: []
|
* routing?: array<string, array{ // Default: []
|
||||||
* service?: scalar|Param|null,
|
* service: scalar|Param|null,
|
||||||
* secret?: scalar|Param|null, // Default: ""
|
* secret?: scalar|Param|null, // Default: ""
|
||||||
* }>,
|
* }>,
|
||||||
* },
|
* },
|
||||||
@@ -754,8 +754,8 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* }>,
|
* }>,
|
||||||
* },
|
* },
|
||||||
* ldap?: array{
|
* ldap?: array{
|
||||||
* service?: scalar|Param|null,
|
* service: scalar|Param|null,
|
||||||
* base_dn?: scalar|Param|null,
|
* base_dn: scalar|Param|null,
|
||||||
* search_dn?: scalar|Param|null, // Default: null
|
* search_dn?: scalar|Param|null, // Default: null
|
||||||
* search_password?: scalar|Param|null, // Default: null
|
* search_password?: scalar|Param|null, // Default: null
|
||||||
* extra_fields?: list<scalar|Param|null>,
|
* extra_fields?: list<scalar|Param|null>,
|
||||||
@@ -766,7 +766,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* password_attribute?: scalar|Param|null, // Default: null
|
* password_attribute?: scalar|Param|null, // Default: null
|
||||||
* },
|
* },
|
||||||
* entity?: array{
|
* entity?: array{
|
||||||
* class?: scalar|Param|null, // The full entity class name of your user class.
|
* class: scalar|Param|null, // The full entity class name of your user class.
|
||||||
* property?: scalar|Param|null, // Default: null
|
* property?: scalar|Param|null, // Default: null
|
||||||
* manager_name?: scalar|Param|null, // Default: null
|
* manager_name?: scalar|Param|null, // Default: null
|
||||||
* },
|
* },
|
||||||
@@ -774,7 +774,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* class?: scalar|Param|null, // Default: "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\User\\JWTUser"
|
* class?: scalar|Param|null, // Default: "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\User\\JWTUser"
|
||||||
* },
|
* },
|
||||||
* }>,
|
* }>,
|
||||||
* firewalls?: array<string, array{ // Default: []
|
* firewalls: array<string, array{ // Default: []
|
||||||
* pattern?: scalar|Param|null,
|
* pattern?: scalar|Param|null,
|
||||||
* host?: scalar|Param|null,
|
* host?: scalar|Param|null,
|
||||||
* methods?: list<scalar|Param|null>,
|
* methods?: list<scalar|Param|null>,
|
||||||
@@ -836,9 +836,9 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* authenticator?: scalar|Param|null, // Default: "lexik_jwt_authentication.security.jwt_authenticator"
|
* authenticator?: scalar|Param|null, // Default: "lexik_jwt_authentication.security.jwt_authenticator"
|
||||||
* },
|
* },
|
||||||
* login_link?: array{
|
* login_link?: array{
|
||||||
* check_route?: scalar|Param|null, // Route that will validate the login link - e.g. "app_login_link_verify".
|
* check_route: scalar|Param|null, // Route that will validate the login link - e.g. "app_login_link_verify".
|
||||||
* check_post_only?: scalar|Param|null, // If true, only HTTP POST requests to "check_route" will be handled by the authenticator. // Default: false
|
* check_post_only?: scalar|Param|null, // If true, only HTTP POST requests to "check_route" will be handled by the authenticator. // Default: false
|
||||||
* signature_properties?: list<scalar|Param|null>,
|
* signature_properties: list<scalar|Param|null>,
|
||||||
* lifetime?: int|Param, // The lifetime of the login link in seconds. // Default: 600
|
* lifetime?: int|Param, // The lifetime of the login link in seconds. // Default: 600
|
||||||
* max_uses?: int|Param, // Max number of times a login link can be used - null means unlimited within lifetime. // Default: null
|
* max_uses?: int|Param, // Max number of times a login link can be used - null means unlimited within lifetime. // Default: null
|
||||||
* used_link_cache?: scalar|Param|null, // Cache service id used to expired links of max_uses is set.
|
* used_link_cache?: scalar|Param|null, // Cache service id used to expired links of max_uses is set.
|
||||||
@@ -940,13 +940,13 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* failure_handler?: scalar|Param|null,
|
* failure_handler?: scalar|Param|null,
|
||||||
* realm?: scalar|Param|null, // Default: null
|
* realm?: scalar|Param|null, // Default: null
|
||||||
* token_extractors?: list<scalar|Param|null>,
|
* token_extractors?: list<scalar|Param|null>,
|
||||||
* token_handler?: string|array{
|
* token_handler: string|array{
|
||||||
* id?: scalar|Param|null,
|
* id?: scalar|Param|null,
|
||||||
* oidc_user_info?: string|array{
|
* oidc_user_info?: string|array{
|
||||||
* base_uri?: scalar|Param|null, // Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured).
|
* base_uri: scalar|Param|null, // Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured).
|
||||||
* discovery?: array{ // Enable the OIDC discovery.
|
* discovery?: array{ // Enable the OIDC discovery.
|
||||||
* cache?: array{
|
* cache?: array{
|
||||||
* id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
|
* id: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
|
||||||
* },
|
* },
|
||||||
* },
|
* },
|
||||||
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g. sub, email, etc.). // Default: "sub"
|
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g. sub, email, etc.). // Default: "sub"
|
||||||
@@ -954,25 +954,25 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* },
|
* },
|
||||||
* oidc?: array{
|
* oidc?: array{
|
||||||
* discovery?: array{ // Enable the OIDC discovery.
|
* discovery?: array{ // Enable the OIDC discovery.
|
||||||
* base_uri?: list<scalar|Param|null>,
|
* base_uri: list<scalar|Param|null>,
|
||||||
* cache?: array{
|
* cache?: array{
|
||||||
* id?: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
|
* id: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration.
|
||||||
* },
|
* },
|
||||||
* },
|
* },
|
||||||
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g.: sub, email..). // Default: "sub"
|
* claim?: scalar|Param|null, // Claim which contains the user identifier (e.g.: sub, email..). // Default: "sub"
|
||||||
* audience?: scalar|Param|null, // Audience set in the token, for validation purpose.
|
* audience: scalar|Param|null, // Audience set in the token, for validation purpose.
|
||||||
* issuers?: list<scalar|Param|null>,
|
* issuers: list<scalar|Param|null>,
|
||||||
* algorithms?: list<scalar|Param|null>,
|
* algorithms: list<scalar|Param|null>,
|
||||||
* keyset?: scalar|Param|null, // JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys).
|
* keyset?: scalar|Param|null, // JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys).
|
||||||
* encryption?: bool|array{
|
* encryption?: bool|array{
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* enforce?: bool|Param, // When enabled, the token shall be encrypted. // Default: false
|
* enforce?: bool|Param, // When enabled, the token shall be encrypted. // Default: false
|
||||||
* algorithms?: list<scalar|Param|null>,
|
* algorithms: list<scalar|Param|null>,
|
||||||
* keyset?: scalar|Param|null, // JSON-encoded JWKSet used to decrypt the token (must contain a list of valid private keys).
|
* keyset: scalar|Param|null, // JSON-encoded JWKSet used to decrypt the token (must contain a list of valid private keys).
|
||||||
* },
|
* },
|
||||||
* },
|
* },
|
||||||
* cas?: array{
|
* cas?: array{
|
||||||
* validation_url?: scalar|Param|null, // CAS server validation URL
|
* validation_url: scalar|Param|null, // CAS server validation URL
|
||||||
* prefix?: scalar|Param|null, // CAS prefix // Default: "cas"
|
* prefix?: scalar|Param|null, // CAS prefix // Default: "cas"
|
||||||
* http_client?: scalar|Param|null, // HTTP Client service // Default: null
|
* http_client?: scalar|Param|null, // HTTP Client service // Default: null
|
||||||
* },
|
* },
|
||||||
@@ -1036,7 +1036,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* dbal?: array{
|
* dbal?: array{
|
||||||
* default_connection?: scalar|Param|null,
|
* default_connection?: scalar|Param|null,
|
||||||
* types?: array<string, string|array{ // Default: []
|
* types?: array<string, string|array{ // Default: []
|
||||||
* class?: scalar|Param|null,
|
* class: scalar|Param|null,
|
||||||
* }>,
|
* }>,
|
||||||
* driver_schemes?: array<string, scalar|Param|null>,
|
* driver_schemes?: array<string, scalar|Param|null>,
|
||||||
* connections?: array<string, array{ // Default: []
|
* connections?: array<string, array{ // Default: []
|
||||||
@@ -1207,7 +1207,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* datetime_functions?: array<string, scalar|Param|null>,
|
* datetime_functions?: array<string, scalar|Param|null>,
|
||||||
* },
|
* },
|
||||||
* filters?: array<string, string|array{ // Default: []
|
* filters?: array<string, string|array{ // Default: []
|
||||||
* class?: scalar|Param|null,
|
* class: scalar|Param|null,
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* parameters?: array<string, mixed>,
|
* parameters?: array<string, mixed>,
|
||||||
* }>,
|
* }>,
|
||||||
@@ -1320,14 +1320,14 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* access_token_issuance?: bool|array{
|
* access_token_issuance?: bool|array{
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* signature?: array{
|
* signature?: array{
|
||||||
* algorithm?: scalar|Param|null, // The algorithm use to sign the access tokens.
|
* algorithm: scalar|Param|null, // The algorithm use to sign the access tokens.
|
||||||
* key?: scalar|Param|null, // The signature key. It shall be JWK encoded.
|
* key: scalar|Param|null, // The signature key. It shall be JWK encoded.
|
||||||
* },
|
* },
|
||||||
* encryption?: bool|array{
|
* encryption?: bool|array{
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
* key_encryption_algorithm?: scalar|Param|null, // The key encryption algorithm is used to encrypt the token.
|
* key_encryption_algorithm: scalar|Param|null, // The key encryption algorithm is used to encrypt the token.
|
||||||
* content_encryption_algorithm?: scalar|Param|null, // The key encryption algorithm is used to encrypt the token.
|
* content_encryption_algorithm: scalar|Param|null, // The key encryption algorithm is used to encrypt the token.
|
||||||
* key?: scalar|Param|null, // The encryption key. It shall be JWK encoded.
|
* key: scalar|Param|null, // The encryption key. It shall be JWK encoded.
|
||||||
* },
|
* },
|
||||||
* },
|
* },
|
||||||
* access_token_verification?: bool|array{
|
* access_token_verification?: bool|array{
|
||||||
@@ -1337,7 +1337,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* claim_checkers?: list<scalar|Param|null>,
|
* claim_checkers?: list<scalar|Param|null>,
|
||||||
* mandatory_claims?: list<scalar|Param|null>,
|
* mandatory_claims?: list<scalar|Param|null>,
|
||||||
* allowed_algorithms?: list<scalar|Param|null>,
|
* allowed_algorithms?: list<scalar|Param|null>,
|
||||||
* keyset?: scalar|Param|null, // The signature keyset. It shall be JWKSet encoded.
|
* keyset: scalar|Param|null, // The signature keyset. It shall be JWKSet encoded.
|
||||||
* },
|
* },
|
||||||
* encryption?: bool|array{
|
* encryption?: bool|array{
|
||||||
* enabled?: bool|Param, // Default: false
|
* enabled?: bool|Param, // Default: false
|
||||||
@@ -1345,7 +1345,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* header_checkers?: list<scalar|Param|null>,
|
* header_checkers?: list<scalar|Param|null>,
|
||||||
* allowed_key_encryption_algorithms?: list<scalar|Param|null>,
|
* allowed_key_encryption_algorithms?: list<scalar|Param|null>,
|
||||||
* allowed_content_encryption_algorithms?: list<scalar|Param|null>,
|
* allowed_content_encryption_algorithms?: list<scalar|Param|null>,
|
||||||
* keyset?: scalar|Param|null, // The encryption keyset. It shall be JWKSet encoded.
|
* keyset: scalar|Param|null, // The encryption keyset. It shall be JWKSet encoded.
|
||||||
* },
|
* },
|
||||||
* },
|
* },
|
||||||
* blocklist_token?: bool|array{
|
* blocklist_token?: bool|array{
|
||||||
@@ -1489,7 +1489,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* },
|
* },
|
||||||
* termsOfService?: scalar|Param|null, // A URL to the Terms of Service for the API. MUST be in the format of a URL. // Default: null
|
* termsOfService?: scalar|Param|null, // A URL to the Terms of Service for the API. MUST be in the format of a URL. // Default: null
|
||||||
* tags?: list<array{ // Default: []
|
* tags?: list<array{ // Default: []
|
||||||
* name?: scalar|Param|null,
|
* name: scalar|Param|null,
|
||||||
* description?: scalar|Param|null, // Default: null
|
* description?: scalar|Param|null, // Default: null
|
||||||
* }>,
|
* }>,
|
||||||
* license?: array{
|
* license?: array{
|
||||||
@@ -1605,14 +1605,14 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* name?: mixed,
|
* name?: mixed,
|
||||||
* allow_create?: mixed,
|
* allow_create?: mixed,
|
||||||
* item_uri_template?: mixed,
|
* item_uri_template?: mixed,
|
||||||
* ...<string, mixed>
|
* ...<mixed>
|
||||||
* },
|
* },
|
||||||
* }
|
* }
|
||||||
* @psalm-type MonologConfig = array{
|
* @psalm-type MonologConfig = array{
|
||||||
* use_microseconds?: scalar|Param|null, // Default: true
|
* use_microseconds?: scalar|Param|null, // Default: true
|
||||||
* channels?: list<scalar|Param|null>,
|
* channels?: list<scalar|Param|null>,
|
||||||
* handlers?: array<string, array{ // Default: []
|
* handlers?: array<string, array{ // Default: []
|
||||||
* type?: scalar|Param|null,
|
* type: scalar|Param|null,
|
||||||
* id?: scalar|Param|null,
|
* id?: scalar|Param|null,
|
||||||
* enabled?: bool|Param, // Default: true
|
* enabled?: bool|Param, // Default: true
|
||||||
* priority?: scalar|Param|null, // Default: 0
|
* priority?: scalar|Param|null, // Default: 0
|
||||||
@@ -1735,7 +1735,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* headers?: list<scalar|Param|null>,
|
* headers?: list<scalar|Param|null>,
|
||||||
* mailer?: scalar|Param|null, // Default: null
|
* mailer?: scalar|Param|null, // Default: null
|
||||||
* email_prototype?: string|array{
|
* email_prototype?: string|array{
|
||||||
* id?: scalar|Param|null,
|
* id: scalar|Param|null,
|
||||||
* method?: scalar|Param|null, // Default: null
|
* method?: scalar|Param|null, // Default: null
|
||||||
* },
|
* },
|
||||||
* verbosity_levels?: array{
|
* verbosity_levels?: array{
|
||||||
@@ -1752,14 +1752,14 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* }>,
|
* }>,
|
||||||
* }
|
* }
|
||||||
* @psalm-type EdnotifConfig = array{
|
* @psalm-type EdnotifConfig = array{
|
||||||
* guichet_wsdl?: scalar|Param|null, // Default: "/var/www/html/vendor/malio/ednotif-bundle/resources/ednotif-ws/WsGuichet.wsdl"
|
* guichet_wsdl: scalar|Param|null,
|
||||||
* metier_wsdl?: scalar|Param|null, // Default: "/var/www/html/vendor/malio/ednotif-bundle/resources/ednotif-ws/wsIpBNotif.wsdl"
|
* metier_wsdl: scalar|Param|null,
|
||||||
* exploitation_code?: scalar|Param|null,
|
* exploitation_code: scalar|Param|null,
|
||||||
* zone?: scalar|Param|null, // Default: null
|
* zone?: scalar|Param|null, // Default: null
|
||||||
* application?: scalar|Param|null, // Default: null
|
* application?: scalar|Param|null, // Default: null
|
||||||
* login?: scalar|Param|null,
|
* login: scalar|Param|null,
|
||||||
* password?: scalar|Param|null,
|
* password: scalar|Param|null,
|
||||||
* exploitation_number?: scalar|Param|null,
|
* exploitation_number: scalar|Param|null,
|
||||||
* exploitation_country_code?: scalar|Param|null, // Default: "FR"
|
* exploitation_country_code?: scalar|Param|null, // Default: "FR"
|
||||||
* token_ttl_seconds?: int|Param, // Default: 900
|
* token_ttl_seconds?: int|Param, // Default: 900
|
||||||
* soap_options?: array{
|
* soap_options?: array{
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.0.87'
|
app.version: '0.0.81'
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="relative border border-slate-200">
|
|
||||||
<div
|
|
||||||
class="grid items-center gap-6 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide"
|
|
||||||
:style="{ gridTemplateColumns: gridCols }"
|
|
||||||
>
|
|
||||||
<div v-for="col in columns" :key="col.key" class="min-w-0">
|
|
||||||
<slot :name="`header-${col.key}`" :column="col">{{ col.label }}</slot>
|
|
||||||
</div>
|
|
||||||
<div v-if="showActions" class="min-w-0">
|
|
||||||
<slot name="header-actions">Actions</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :class="dimRows ? 'opacity-50 transition-opacity' : ''" :aria-busy="loading || undefined">
|
|
||||||
<template v-if="paginatedItems.length">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in paginatedItems"
|
|
||||||
:key="item.id ?? index"
|
|
||||||
class="grid gap-6 px-4 py-3 text-sm border-t border-slate-200"
|
|
||||||
:class="rowClickable ? 'hover:bg-slate-50 cursor-pointer' : ''"
|
|
||||||
:style="{ gridTemplateColumns: gridCols }"
|
|
||||||
:role="rowClickable ? 'button' : undefined"
|
|
||||||
:tabindex="rowClickable ? 0 : undefined"
|
|
||||||
@click="onRowClick(item)"
|
|
||||||
@keydown.enter="onRowClick(item)"
|
|
||||||
@keydown.space.prevent="onRowClick(item)"
|
|
||||||
>
|
|
||||||
<div v-for="col in columns" :key="col.key" class="min-w-0 truncate">
|
|
||||||
<slot :name="`cell-${col.key}`" :item="item" :column="col">
|
|
||||||
{{ getNestedValue(item, col.key) }}
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<div v-if="showActions" @click.stop>
|
|
||||||
<slot name="actions" :item="item" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div
|
|
||||||
v-else-if="loading"
|
|
||||||
class="flex items-center justify-center border-t border-slate-200 px-4 py-8 text-primary-500"
|
|
||||||
role="status"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<UiLoadingDots />
|
|
||||||
<span class="sr-only">Chargement…</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="border-t border-slate-200 px-4 py-8 text-center text-sm text-slate-500"
|
|
||||||
>
|
|
||||||
<slot name="empty">{{ emptyMessage }}</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="dimRows"
|
|
||||||
class="pointer-events-none absolute inset-0 flex items-center justify-center"
|
|
||||||
role="status"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
<div class="rounded bg-white/80 px-4 py-2 text-primary-500 shadow">
|
|
||||||
<UiLoadingDots />
|
|
||||||
<span class="sr-only">Chargement…</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="total > 0" class="flex justify-between pt-2">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<label :for="perPageId" class="whitespace-nowrap text-sm text-slate-700">Lignes :</label>
|
|
||||||
<select
|
|
||||||
:id="perPageId"
|
|
||||||
:value="currentPerPage"
|
|
||||||
class="h-10 rounded border border-slate-300 bg-white px-2 text-sm text-primary-700"
|
|
||||||
@change="onPerPageChange(($event.target as HTMLSelectElement).value)"
|
|
||||||
>
|
|
||||||
<option v-for="n in perPageOptions" :key="n" :value="n">{{ n }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav aria-label="Pagination" class="flex items-center gap-1">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="h-10 px-3 text-sm text-primary-500 hover:underline disabled:cursor-not-allowed disabled:text-slate-400 disabled:no-underline"
|
|
||||||
:disabled="currentPage <= 1"
|
|
||||||
aria-label="Page précédente"
|
|
||||||
@click="goToPage(currentPage - 1)"
|
|
||||||
>
|
|
||||||
Prev
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<template v-for="(entry, i) in visiblePages" :key="`${typeof entry}-${entry}-${i}`">
|
|
||||||
<span
|
|
||||||
v-if="entry === '...'"
|
|
||||||
class="px-1 text-sm text-slate-400"
|
|
||||||
aria-hidden="true"
|
|
||||||
>…</span>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
type="button"
|
|
||||||
class="h-10 min-w-[2.5rem] rounded px-2 text-sm transition-colors"
|
|
||||||
:class="entry === currentPage
|
|
||||||
? 'bg-primary-500 font-semibold text-white'
|
|
||||||
: 'text-slate-700 hover:bg-slate-100'"
|
|
||||||
:aria-current="entry === currentPage ? 'page' : undefined"
|
|
||||||
@click="goToPage(entry)"
|
|
||||||
>
|
|
||||||
{{ entry }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="h-10 px-3 text-sm text-primary-500 hover:underline disabled:cursor-not-allowed disabled:text-slate-400 disabled:no-underline"
|
|
||||||
:disabled="currentPage >= totalPages"
|
|
||||||
aria-label="Page suivante"
|
|
||||||
@click="goToPage(currentPage + 1)"
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
||||||
import { computed, useId } from 'vue'
|
|
||||||
|
|
||||||
interface Column {
|
|
||||||
key: string
|
|
||||||
label: string
|
|
||||||
width?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
|
||||||
columns: Column[]
|
|
||||||
items: T[]
|
|
||||||
totalItems?: number
|
|
||||||
page?: number
|
|
||||||
perPage?: number
|
|
||||||
perPageOptions?: number[]
|
|
||||||
rowClickable?: boolean
|
|
||||||
showActions?: boolean
|
|
||||||
emptyMessage?: string
|
|
||||||
loading?: boolean
|
|
||||||
}>(), {
|
|
||||||
totalItems: undefined,
|
|
||||||
page: 1,
|
|
||||||
perPage: 10,
|
|
||||||
perPageOptions: () => [10, 25, 50],
|
|
||||||
rowClickable: false,
|
|
||||||
showActions: false,
|
|
||||||
emptyMessage: 'Aucune donnée',
|
|
||||||
loading: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:page', value: number): void
|
|
||||||
(e: 'update:perPage', value: number): void
|
|
||||||
(e: 'row-click', item: T): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const perPageId = useId()
|
|
||||||
|
|
||||||
const currentPage = computed(() => props.page)
|
|
||||||
const currentPerPage = computed(() => props.perPage)
|
|
||||||
|
|
||||||
const isServerSide = computed(() => props.totalItems !== undefined)
|
|
||||||
const total = computed(() => props.totalItems ?? props.items.length)
|
|
||||||
|
|
||||||
const totalPages = computed(() =>
|
|
||||||
Math.max(1, Math.ceil(total.value / currentPerPage.value))
|
|
||||||
)
|
|
||||||
|
|
||||||
const paginatedItems = computed(() => {
|
|
||||||
if (isServerSide.value) return props.items
|
|
||||||
const start = (currentPage.value - 1) * currentPerPage.value
|
|
||||||
return props.items.slice(start, start + currentPerPage.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
const gridCols = computed(() => {
|
|
||||||
const dataCols = props.columns.map(c => c.width ?? '1fr').join(' ')
|
|
||||||
return props.showActions ? `${dataCols} 60px` : dataCols
|
|
||||||
})
|
|
||||||
|
|
||||||
const dimRows = computed(() => props.loading && paginatedItems.value.length > 0)
|
|
||||||
|
|
||||||
const visiblePages = computed<(number | '...')[]>(() => {
|
|
||||||
const tp = totalPages.value
|
|
||||||
const cp = currentPage.value
|
|
||||||
|
|
||||||
if (tp <= 5) {
|
|
||||||
return Array.from({ length: tp }, (_, i) => i + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const pages: (number | '...')[] = []
|
|
||||||
pages.push(1)
|
|
||||||
|
|
||||||
if (cp > 3) pages.push('...')
|
|
||||||
|
|
||||||
const start = Math.max(2, cp - 1)
|
|
||||||
const end = Math.min(tp - 1, cp + 1)
|
|
||||||
for (let i = start; i <= end; i++) pages.push(i)
|
|
||||||
|
|
||||||
if (cp < tp - 2) pages.push('...')
|
|
||||||
|
|
||||||
if (tp > 1) pages.push(tp)
|
|
||||||
|
|
||||||
return pages
|
|
||||||
})
|
|
||||||
|
|
||||||
const goToPage = (n: number) => {
|
|
||||||
if (n < 1 || n > totalPages.value || n === currentPage.value) return
|
|
||||||
emit('update:page', n)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onPerPageChange = (value: string) => {
|
|
||||||
emit('update:perPage', Number(value))
|
|
||||||
emit('update:page', 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRowClick = (item: T) => {
|
|
||||||
if (!props.rowClickable) return
|
|
||||||
emit('row-click', item)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getNestedValue = (obj: any, path: string): string => {
|
|
||||||
const value = path.split('.').reduce((acc, key) => acc?.[key], obj)
|
|
||||||
return value ?? '—'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -14,9 +14,8 @@
|
|||||||
:value="modelValue ?? ''"
|
:value="modelValue ?? ''"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="w-full min-w-0 border-b border-primary-700 justify-self-start text-primary-700 bg-transparent appearance-none"
|
class="border-b border-primary-700 justify-self-start text-xl text-primary-700 py-[6px] uppercase bg-transparent appearance-none h-[34px]"
|
||||||
:class="[
|
:class="[
|
||||||
sizeClass,
|
|
||||||
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
inputClass
|
inputClass
|
||||||
@@ -37,14 +36,12 @@ const props = withDefaults(
|
|||||||
label?: string
|
label?: string
|
||||||
modelValue: string | null | undefined
|
modelValue: string | null | undefined
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
size?: 'default' | 'compact'
|
|
||||||
wrapperClass?: string
|
wrapperClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
inputClass?: string
|
inputClass?: string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
disabled: false,
|
disabled: false,
|
||||||
size: 'default',
|
|
||||||
wrapperClass: '',
|
wrapperClass: '',
|
||||||
labelClass: '',
|
labelClass: '',
|
||||||
inputClass: ''
|
inputClass: ''
|
||||||
@@ -57,11 +54,6 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
const isEmpty = computed(() => !props.modelValue)
|
const isEmpty = computed(() => !props.modelValue)
|
||||||
const sizeClass = computed(() =>
|
|
||||||
props.size === 'compact'
|
|
||||||
? 'text-sm h-8 font-normal normal-case tracking-normal'
|
|
||||||
: 'text-xl py-[6px] uppercase h-[34px]'
|
|
||||||
)
|
|
||||||
|
|
||||||
const onInput = (event: Event) => {
|
const onInput = (event: Event) => {
|
||||||
const target = event.target as HTMLInputElement
|
const target = event.target as HTMLInputElement
|
||||||
|
|||||||
@@ -13,16 +13,15 @@
|
|||||||
:value="modelValue ?? ''"
|
:value="modelValue ?? ''"
|
||||||
:disabled="disabled || loading"
|
:disabled="disabled || loading"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="w-full min-w-0 border-b border-primary-700 justify-self-start text-primary-700 bg-transparent"
|
class="border-b border-primary-700 justify-self-start text-xl text-primary-700 py-[6px] bg-transparent"
|
||||||
:class="[
|
:class="[
|
||||||
sizeClass,
|
|
||||||
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
||||||
disabled || loading ? 'cursor-not-allowed' : 'cursor-pointer',
|
disabled || loading ? 'cursor-not-allowed' : 'cursor-pointer',
|
||||||
selectClass
|
selectClass
|
||||||
]"
|
]"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
>
|
>
|
||||||
<option value="" class="text-neutral-400">
|
<option value="" disabled class="text-neutral-400">
|
||||||
{{ placeholderText }}
|
{{ placeholderText }}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
@@ -56,7 +55,6 @@ const props = withDefaults(
|
|||||||
options: SelectOption[]
|
options: SelectOption[]
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
size?: 'default' | 'compact'
|
|
||||||
wrapperClass?: string
|
wrapperClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
selectClass?: string
|
selectClass?: string
|
||||||
@@ -65,7 +63,6 @@ const props = withDefaults(
|
|||||||
placeholder: 'Sélectionner',
|
placeholder: 'Sélectionner',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
size: 'default',
|
|
||||||
wrapperClass: '',
|
wrapperClass: '',
|
||||||
labelClass: '',
|
labelClass: '',
|
||||||
selectClass: ''
|
selectClass: ''
|
||||||
@@ -80,11 +77,6 @@ const attrs = useAttrs()
|
|||||||
|
|
||||||
const isEmpty = computed(() => props.modelValue === '' || props.modelValue === null || props.modelValue === undefined)
|
const isEmpty = computed(() => props.modelValue === '' || props.modelValue === null || props.modelValue === undefined)
|
||||||
const placeholderText = computed(() => props.placeholder || 'Sélectionner')
|
const placeholderText = computed(() => props.placeholder || 'Sélectionner')
|
||||||
const sizeClass = computed(() =>
|
|
||||||
props.size === 'compact'
|
|
||||||
? 'text-sm h-8 font-normal normal-case tracking-normal'
|
|
||||||
: 'text-xl py-[6px]'
|
|
||||||
)
|
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
const target = event.target as HTMLSelectElement
|
const target = event.target as HTMLSelectElement
|
||||||
|
|||||||
@@ -16,10 +16,9 @@
|
|||||||
:maxlength="maxlength"
|
:maxlength="maxlength"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
class="w-full min-w-0 border-b border-primary-700 bg-transparent"
|
class="border-b border-black text-xl py-[6px] bg-transparent text-primary-700"
|
||||||
:class="[
|
:class="[
|
||||||
sizeClass,
|
isEmpty ? 'text-neutral-400' : 'text-black',
|
||||||
isEmpty ? 'text-neutral-400' : 'text-primary-700',
|
|
||||||
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
disabled ? 'cursor-not-allowed' : 'cursor-text',
|
||||||
inputClass
|
inputClass
|
||||||
]"
|
]"
|
||||||
@@ -41,7 +40,6 @@ const props = withDefaults(
|
|||||||
placeholder?: string
|
placeholder?: string
|
||||||
maxlength?: number | string
|
maxlength?: number | string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
size?: 'default' | 'compact'
|
|
||||||
wrapperClass?: string
|
wrapperClass?: string
|
||||||
labelClass?: string
|
labelClass?: string
|
||||||
inputClass?: string
|
inputClass?: string
|
||||||
@@ -50,7 +48,6 @@ const props = withDefaults(
|
|||||||
placeholder: '',
|
placeholder: '',
|
||||||
maxlength: undefined,
|
maxlength: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
size: 'default',
|
|
||||||
wrapperClass: '',
|
wrapperClass: '',
|
||||||
labelClass: '',
|
labelClass: '',
|
||||||
inputClass: ''
|
inputClass: ''
|
||||||
@@ -63,11 +60,6 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
const isEmpty = computed(() => !props.modelValue)
|
const isEmpty = computed(() => !props.modelValue)
|
||||||
const sizeClass = computed(() =>
|
|
||||||
props.size === 'compact'
|
|
||||||
? 'text-sm h-8 font-normal normal-case tracking-normal'
|
|
||||||
: 'text-xl py-[6px]'
|
|
||||||
)
|
|
||||||
|
|
||||||
const onInput = (event: Event) => {
|
const onInput = (event: Event) => {
|
||||||
const target = event.target as HTMLInputElement
|
const target = event.target as HTMLInputElement
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
:placeholder="placeholderText"
|
:placeholder="placeholderText"
|
||||||
:required="required"
|
:required="required"
|
||||||
class="border-b border-primary-700 flex-1 min-w-0 text-xl text-primary-500 uppercase h-[36px] py-[6px]"
|
class="border-b border-black flex-1 min-w-0 text-xl text-primary-500 uppercase h-[36px] py-[6px]"
|
||||||
@input="handleInput"
|
@input="handleInput"
|
||||||
/>
|
/>
|
||||||
<UiCheckbox
|
<UiCheckbox
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import { ref, watch } from 'vue'
|
|
||||||
import { useApi } from '~/composables/useApi'
|
|
||||||
|
|
||||||
type FilterValue = string | number | boolean | null
|
|
||||||
|
|
||||||
export interface UseDataTableServerStateOptions {
|
|
||||||
initialPerPage?: number
|
|
||||||
debounceMs?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDataTableServerState<T = Record<string, unknown>>(
|
|
||||||
endpoint: string,
|
|
||||||
initialFilters: Record<string, FilterValue> = {},
|
|
||||||
options: UseDataTableServerStateOptions = {}
|
|
||||||
) {
|
|
||||||
const api = useApi()
|
|
||||||
|
|
||||||
const debounceMs = options.debounceMs ?? 300
|
|
||||||
const initialPerPage = options.initialPerPage ?? 10
|
|
||||||
|
|
||||||
const items = ref<T[]>([]) as { value: T[] }
|
|
||||||
const totalItems = ref(0)
|
|
||||||
const page = ref(1)
|
|
||||||
const perPage = ref(initialPerPage)
|
|
||||||
const filters = ref<Record<string, FilterValue>>({ ...initialFilters })
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
let requestToken = 0
|
|
||||||
|
|
||||||
const buildQueryParams = (): Record<string, string | number | boolean> => {
|
|
||||||
const params: Record<string, string | number | boolean> = {
|
|
||||||
page: page.value,
|
|
||||||
itemsPerPage: perPage.value
|
|
||||||
}
|
|
||||||
for (const [key, value] of Object.entries(filters.value)) {
|
|
||||||
if (value === '' || value === null) continue
|
|
||||||
params[key] = value as string | number | boolean
|
|
||||||
}
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchItems = async (): Promise<void> => {
|
|
||||||
const currentToken = ++requestToken
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await api.get<{ member: T[]; totalItems: number }>(
|
|
||||||
endpoint,
|
|
||||||
buildQueryParams(),
|
|
||||||
{
|
|
||||||
toast: false,
|
|
||||||
headers: { Accept: 'application/ld+json' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (currentToken !== requestToken) return
|
|
||||||
items.value = data?.member ?? []
|
|
||||||
totalItems.value = data?.totalItems ?? 0
|
|
||||||
} finally {
|
|
||||||
if (currentToken === requestToken) {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reload = (): void => {
|
|
||||||
if (debounceTimer) {
|
|
||||||
clearTimeout(debounceTimer)
|
|
||||||
debounceTimer = null
|
|
||||||
}
|
|
||||||
void fetchItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
const scheduleReload = (): void => {
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer)
|
|
||||||
debounceTimer = setTimeout(() => {
|
|
||||||
debounceTimer = null
|
|
||||||
void fetchItems()
|
|
||||||
}, debounceMs)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch([page, perPage], () => {
|
|
||||||
reload()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(filters, () => {
|
|
||||||
if (page.value !== 1) {
|
|
||||||
page.value = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
scheduleReload()
|
|
||||||
}, { deep: true })
|
|
||||||
|
|
||||||
return {
|
|
||||||
items,
|
|
||||||
totalItems,
|
|
||||||
page,
|
|
||||||
perPage,
|
|
||||||
filters,
|
|
||||||
loading,
|
|
||||||
reload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between ">
|
||||||
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des types bovins</h1>
|
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des types bovins</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-7 border border-slate-200 mb-11 ">
|
||||||
<div v-if="auth.isAdmin" class="mt-7 mb-11">
|
<div class="grid grid-cols-2 gap-4 text-primary-700 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
<UiDataTable
|
<div>Nom</div>
|
||||||
v-model:page="page"
|
<div>Code</div>
|
||||||
v-model:per-page="perPage"
|
</div>
|
||||||
:columns="columns"
|
<div v-if="!auth.isAdmin" class="px-4 py-6 text-slate-400">
|
||||||
:items="items"
|
Accès réservé aux administrateurs.
|
||||||
:total-items="totalItems"
|
</div>
|
||||||
:loading="loading"
|
<div v-else-if="bovinList.length === 0" class="px-4 py-6 text-slate-400">
|
||||||
row-clickable
|
Aucun type de bovin.
|
||||||
@row-click="goToBovin"
|
</div>
|
||||||
>
|
<template v-else>
|
||||||
<template #header-label>
|
<div
|
||||||
<UiTextInput v-model="filters.label" placeholder="Nom" size="compact" />
|
v-for="bovin in bovinList"
|
||||||
</template>
|
:key="bovin.id"
|
||||||
<template #header-code>
|
class="grid grid-cols-2 text-primary-700 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
|
||||||
<UiTextInput v-model="filters.code" placeholder="Code" size="compact" />
|
role="button"
|
||||||
</template>
|
tabindex="0"
|
||||||
</UiDataTable>
|
@click="goToBovin(bovin.id)"
|
||||||
|
@keydown.enter="goToBovin(bovin.id)"
|
||||||
|
>
|
||||||
|
<div>{{ bovin.label }}</div>
|
||||||
|
<div>{{ bovin.code }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
|
||||||
Accès réservé aux administrateurs.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/admin/bovin"
|
to="/admin/bovin"
|
||||||
@@ -33,37 +35,24 @@
|
|||||||
:class="auth.isAdmin ? '' : 'cursor-not-allowed opacity-60'"
|
:class="auth.isAdmin ? '' : 'cursor-not-allowed opacity-60'"
|
||||||
@click="handleAddClick"
|
@click="handleAddClick"
|
||||||
>
|
>
|
||||||
<Icon name="mdi:plus" size="28" />
|
<Icon name="mdi:plus" size="28" />
|
||||||
Ajouter
|
Ajouter
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { BovineTypeData } from '~/services/dto/bovine-type-data'
|
import { getBovineTypeList } from "~/services/bovine-type"
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import type { BovineTypeData } from "~/services/dto/bovine-type-data"
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
import { useAuthStore } from "~/stores/auth"
|
||||||
|
|
||||||
|
const bovinList = ref<BovineTypeData[]>([])
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
const goToBovin = (id: number) => {
|
||||||
useDataTableServerState<BovineTypeData>(
|
|
||||||
'bovine_types',
|
|
||||||
{
|
|
||||||
label: '',
|
|
||||||
code: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'label', label: 'Nom' },
|
|
||||||
{ key: 'code', label: 'Code' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const goToBovin = (bovin: BovineTypeData) => {
|
|
||||||
if (!auth.isAdmin) return
|
if (!auth.isAdmin) return
|
||||||
router.push(`/admin/bovin/${bovin.id}`)
|
router.push(`/admin/bovin/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddClick = (event: Event) => {
|
const handleAddClick = (event: Event) => {
|
||||||
@@ -71,7 +60,8 @@ const handleAddClick = (event: Event) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
if (auth.isAdmin) reload()
|
if (!auth.isAdmin) return
|
||||||
|
bovinList.value = await getBovineTypeList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,62 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<h1 class="text-4xl font-bold uppercase text-primary-500">listes des transporteurs</h1>
|
<div class="flex items-center justify-between ">
|
||||||
</div>
|
<h1 class="text-4xl font-bold uppercase text-primary-500">listes des transporteurs</h1>
|
||||||
|
|
||||||
<div class="mt-7 mb-11">
|
|
||||||
<UiDataTable
|
|
||||||
v-model:page="page"
|
|
||||||
v-model:per-page="perPage"
|
|
||||||
:columns="columns"
|
|
||||||
:items="items"
|
|
||||||
:total-items="totalItems"
|
|
||||||
:loading="loading"
|
|
||||||
row-clickable
|
|
||||||
@row-click="goToCarrier"
|
|
||||||
>
|
|
||||||
<template #header-name>
|
|
||||||
<UiTextInput v-model="filters.name" placeholder="Label" size="compact" />
|
|
||||||
</template>
|
|
||||||
<template #header-code>
|
|
||||||
<UiTextInput v-model="filters.code" placeholder="Code" size="compact" />
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-7 border border-slate-200 mb-11 ">
|
||||||
|
<div class="grid grid-cols-2 gap-4 text-primary-700 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
|
<div>Label</div>
|
||||||
|
<div>Code</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="carrier in carrierList"
|
||||||
|
:key="carrier.id"
|
||||||
|
class="grid grid-cols-2 text-primary-700 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
@click="goToCarrier(carrier.id)"
|
||||||
|
@keydown.enter="goToCarrier(carrier.id)"
|
||||||
|
>
|
||||||
|
<div>{{ carrier.name}}</div>
|
||||||
|
<div>{{ carrier.code }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/admin/carrier"
|
to="/admin/carrier"
|
||||||
class="inline-flex items-center justify-center text-xl text-white uppercase bg-primary-500 h-[50px] px-8 rounded hover:opacity-80 gap-2"
|
class="inline-flex items-center justify-center text-xl text-white uppercase bg-primary-500 h-[50px] px-8 rounded hover:opacity-80 gap-2"
|
||||||
>
|
>
|
||||||
<Icon name="mdi:plus" size="28" />
|
<Icon name="mdi:plus" size="28" />
|
||||||
Ajouter
|
Ajouter
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CarrierData } from '~/services/dto/carrier-data'
|
import type {CarrierData} from "~/services/dto/carrier-data";
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
import {getCarrierList} from "~/services/carrier";
|
||||||
|
|
||||||
|
const carrierList = ref<CarrierData[]>()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
const goToCarrier = (id: number) => {
|
||||||
useDataTableServerState<CarrierData>(
|
router.push(`/admin/carrier/${id}`)
|
||||||
'carriers',
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
code: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'name', label: 'Label' },
|
|
||||||
{ key: 'code', label: 'Code' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const goToCarrier = (carrier: CarrierData) => {
|
|
||||||
router.push(`/admin/carrier/${carrier.id}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(reload)
|
onMounted(async () => {
|
||||||
|
carrierList.value = await getCarrierList(false)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,35 +3,37 @@
|
|||||||
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des clients</h1>
|
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des clients</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="auth.isAdmin" class="mt-7 mb-11">
|
<div v-if="auth.isAdmin" class="mt-7 border border-slate-200 mb-11">
|
||||||
<UiDataTable
|
<div class="max-h-96 overflow-y-auto">
|
||||||
v-model:page="page"
|
<div
|
||||||
v-model:per-page="perPage"
|
class="sticky text-primary-700 top-0 z-10 grid grid-cols-4 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide"
|
||||||
:columns="columns"
|
>
|
||||||
:items="items"
|
<div>Nom</div>
|
||||||
:total-items="totalItems"
|
<div>Téléphone</div>
|
||||||
:loading="loading"
|
<div>Mail</div>
|
||||||
row-clickable
|
<div>Créé par</div>
|
||||||
@row-click="goToCustomer"
|
</div>
|
||||||
>
|
|
||||||
<template #header-name>
|
<div v-if="customerList.length === 0" class="px-4 py-6 text-slate-400">
|
||||||
<UiTextInput v-model="filters.name" placeholder="Nom" size="compact" />
|
Aucun client.
|
||||||
</template>
|
</div>
|
||||||
<template #header-phone>
|
|
||||||
<UiTextInput v-model="filters.phone" placeholder="Téléphone" size="compact" />
|
<div
|
||||||
</template>
|
v-for="customer in customerList"
|
||||||
<template #header-email>
|
:key="customer.id"
|
||||||
<UiTextInput v-model="filters.email" placeholder="Mail" size="compact" />
|
class="grid grid-cols-4 text-primary-700 hover:bg-slate-50 border-t gap-4 px-4 py-2 cursor-pointer"
|
||||||
</template>
|
@click="goToCustomer(customer.id)"
|
||||||
<template #header-createdBy.username>
|
>
|
||||||
<UiTextInput v-model="filters['createdBy.username']" placeholder="Créé par" size="compact" />
|
<div class="truncate">{{ customer.name || "—" }}</div>
|
||||||
</template>
|
<div class="truncate">{{ customer.phone || "—" }}</div>
|
||||||
</UiDataTable>
|
<div class="truncate">{{ customer.email || "—" }}</div>
|
||||||
|
<div class="truncate">{{ customer.createdBy?.username || "—" }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
||||||
Accès réservé aux administrateurs.
|
Accès réservé aux administrateurs.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/admin/customer"
|
to="/admin/customer"
|
||||||
@@ -46,34 +48,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CustomerData } from '~/services/dto/customer-data'
|
import { getCustomerList } from "~/services/customer"
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import type { CustomerData } from "~/services/dto/customer-data"
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
import { useAuthStore } from "~/stores/auth"
|
||||||
|
|
||||||
|
const customerList = ref<CustomerData[]>([])
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
const goToCustomer = (id: number) => {
|
||||||
useDataTableServerState<CustomerData>(
|
|
||||||
'customers',
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
phone: '',
|
|
||||||
email: '',
|
|
||||||
'createdBy.username': ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'name', label: 'Nom' },
|
|
||||||
{ key: 'phone', label: 'Téléphone' },
|
|
||||||
{ key: 'email', label: 'Mail' },
|
|
||||||
{ key: 'createdBy.username', label: 'Créé par' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const goToCustomer = (customer: CustomerData) => {
|
|
||||||
if (!auth.isAdmin) return
|
if (!auth.isAdmin) return
|
||||||
router.push(`/admin/customer/${customer.id}`)
|
router.push(`/admin/customer/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddClick = (event: Event) => {
|
const handleAddClick = (event: Event) => {
|
||||||
@@ -81,7 +66,8 @@ const handleAddClick = (event: Event) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
if (auth.isAdmin) reload()
|
if (!auth.isAdmin) return
|
||||||
|
customerList.value = await getCustomerList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,35 +3,37 @@
|
|||||||
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des fournisseurs</h1>
|
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des fournisseurs</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="auth.isAdmin" class="mt-7 mb-11">
|
<div v-if="auth.isAdmin" class="mt-7 border border-slate-200 mb-11">
|
||||||
<UiDataTable
|
<div class="max-h-96 overflow-y-auto">
|
||||||
v-model:page="page"
|
<div
|
||||||
v-model:per-page="perPage"
|
class="sticky text-primary-700 top-0 z-10 grid grid-cols-4 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide"
|
||||||
:columns="columns"
|
>
|
||||||
:items="items"
|
<div>Nom</div>
|
||||||
:total-items="totalItems"
|
<div>Téléphone</div>
|
||||||
:loading="loading"
|
<div>Mail</div>
|
||||||
row-clickable
|
<div>Créé par</div>
|
||||||
@row-click="goToSupplier"
|
</div>
|
||||||
>
|
|
||||||
<template #header-name>
|
<div v-if="supplierList.length === 0" class="px-4 py-6 text-slate-400">
|
||||||
<UiTextInput v-model="filters.name" placeholder="Nom" size="compact" />
|
Aucun fournisseur.
|
||||||
</template>
|
</div>
|
||||||
<template #header-phone>
|
|
||||||
<UiTextInput v-model="filters.phone" placeholder="Téléphone" size="compact" />
|
<div
|
||||||
</template>
|
v-for="supplier in supplierList"
|
||||||
<template #header-email>
|
:key="supplier.id"
|
||||||
<UiTextInput v-model="filters.email" placeholder="Mail" size="compact" />
|
class="grid grid-cols-4 text-primary-700 hover:bg-slate-50 border-t gap-4 px-4 py-2 cursor-pointer"
|
||||||
</template>
|
@click="goToSupplier(supplier.id)"
|
||||||
<template #header-createdBy.username>
|
>
|
||||||
<UiTextInput v-model="filters['createdBy.username']" placeholder="Créé par" size="compact" />
|
<div class="truncate">{{ supplier.name || "—" }}</div>
|
||||||
</template>
|
<div class="truncate">{{ supplier.phone || "—" }}</div>
|
||||||
</UiDataTable>
|
<div class="truncate">{{ supplier.email || "—" }}</div>
|
||||||
|
<div class="truncate">{{ supplier.createdBy?.username || "—" }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
||||||
Accès réservé aux administrateurs.
|
Accès réservé aux administrateurs.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/admin/supplier"
|
to="/admin/supplier"
|
||||||
@@ -46,34 +48,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SupplierData } from '~/services/dto/supplier-data'
|
import { getSupplierList } from "~/services/supplier"
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import type { SupplierData } from "~/services/dto/supplier-data"
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
import { useAuthStore } from "~/stores/auth"
|
||||||
|
|
||||||
|
const supplierList = ref<SupplierData[]>([])
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
const goToSupplier = (id: number) => {
|
||||||
useDataTableServerState<SupplierData>(
|
|
||||||
'suppliers',
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
phone: '',
|
|
||||||
email: '',
|
|
||||||
'createdBy.username': ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'name', label: 'Nom' },
|
|
||||||
{ key: 'phone', label: 'Téléphone' },
|
|
||||||
{ key: 'email', label: 'Mail' },
|
|
||||||
{ key: 'createdBy.username', label: 'Créé par' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const goToSupplier = (supplier: SupplierData) => {
|
|
||||||
if (!auth.isAdmin) return
|
if (!auth.isAdmin) return
|
||||||
router.push(`/admin/supplier/${supplier.id}`)
|
router.push(`/admin/supplier/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddClick = (event: Event) => {
|
const handleAddClick = (event: Event) => {
|
||||||
@@ -81,7 +66,8 @@ const handleAddClick = (event: Event) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
if (auth.isAdmin) reload()
|
if (!auth.isAdmin) return
|
||||||
|
supplierList.value = await getSupplierList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,52 +3,42 @@
|
|||||||
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des utilisateurs</h1>
|
<h1 class="text-4xl font-bold uppercase text-primary-500">Liste des utilisateurs</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="auth.isAdmin" class="mt-7 mb-11">
|
<div v-if="auth.isAdmin" class="mt-7 border border-slate-200 mb-11">
|
||||||
<UiDataTable
|
<div class="grid grid-cols-3 text-primary-700 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
v-model:page="page"
|
<div>Utilisateur</div>
|
||||||
v-model:per-page="perPage"
|
<div>Role</div>
|
||||||
:columns="columns"
|
<div>Statut</div>
|
||||||
:items="items"
|
</div>
|
||||||
:total-items="totalItems"
|
<div v-if="userList.length === 0" class="px-4 py-6 text-slate-400">
|
||||||
:loading="loading"
|
Aucun utilisateur.
|
||||||
row-clickable
|
</div>
|
||||||
@row-click="goToUser"
|
<template v-else>
|
||||||
>
|
<div
|
||||||
<template #header-username>
|
v-for="user in userList"
|
||||||
<UiTextInput
|
:key="user.id"
|
||||||
v-model="filters.username"
|
class="grid grid-cols-3 text-primary-700 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200 items-center"
|
||||||
placeholder="Utilisateur"
|
role="button"
|
||||||
size="compact"
|
tabindex="0"
|
||||||
/>
|
@click="goToUser(user.id)"
|
||||||
</template>
|
@keydown.enter="goToUser(user.id)"
|
||||||
<template #header-roles>
|
>
|
||||||
<UiTextInput :model-value="''" placeholder="Role" size="compact" disabled />
|
<div>{{ user.username }}</div>
|
||||||
</template>
|
<div>{{ getRoleLabels(user.roles) }}</div>
|
||||||
<template #header-isLocked>
|
<div>
|
||||||
<UiSelect
|
<span
|
||||||
v-model="filters.isLocked"
|
v-if="user.isLocked"
|
||||||
placeholder="Statut"
|
class="inline-block px-2 py-0.5 text-xs font-semibold rounded bg-red-100 text-red-700"
|
||||||
:options="statusOptions"
|
>Verrouillé</span>
|
||||||
size="compact"
|
<span
|
||||||
/>
|
v-else
|
||||||
</template>
|
class="inline-block px-2 py-0.5 text-xs font-semibold rounded bg-green-100 text-green-700"
|
||||||
<template #cell-roles="{ item }">
|
>Actif</span>
|
||||||
{{ getRoleLabels(item.roles) }}
|
</div>
|
||||||
</template>
|
</div>
|
||||||
<template #cell-isLocked="{ item }">
|
</template>
|
||||||
<span
|
|
||||||
v-if="item.isLocked"
|
|
||||||
class="inline-block px-2 py-0.5 text-xs font-semibold rounded bg-red-100 text-red-700"
|
|
||||||
>Verrouillé</span>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="inline-block px-2 py-0.5 text-xs font-semibold rounded bg-green-100 text-green-700"
|
|
||||||
>Actif</span>
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
<div v-else class="mt-7 border border-slate-200 mb-11 px-4 py-6 text-slate-400">
|
||||||
Accès réservé aux administrateurs.
|
Acces reserve aux administrateurs.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center items-center">
|
<div class="flex justify-center items-center">
|
||||||
@@ -65,43 +55,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { UserData } from '~/services/dto/user-data'
|
import type { UserData } from "~/services/dto/user-data"
|
||||||
import { ROLE } from '~/utils/constants'
|
import { getAdminUsers } from "~/services/auth"
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import { ROLE } from "~/utils/constants"
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
import { useAuthStore } from "~/stores/auth"
|
||||||
|
|
||||||
|
const userList = ref<UserData[]>([])
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const roleLabelByValue = new Map(ROLE.map(role => [role.value, role.label]))
|
const roleLabelByValue = new Map(ROLE.map((role) => [role.value, role.label]))
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
const goToUser = (id: number) => {
|
||||||
useDataTableServerState<UserData>(
|
|
||||||
'admin/users',
|
|
||||||
{
|
|
||||||
username: '',
|
|
||||||
isLocked: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const statusOptions = [
|
|
||||||
{ value: 'false', label: 'Actif' },
|
|
||||||
{ value: 'true', label: 'Verrouillé' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'username', label: 'Utilisateur' },
|
|
||||||
{ key: 'roles', label: 'Role' },
|
|
||||||
{ key: 'isLocked', label: 'Statut', width: '160px' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const getRoleLabels = (roles?: string[]) => {
|
|
||||||
if (!roles || roles.length === 0) return '---'
|
|
||||||
return roles.map(role => roleLabelByValue.get(role) ?? role).join(', ')
|
|
||||||
}
|
|
||||||
|
|
||||||
const goToUser = (user: UserData) => {
|
|
||||||
if (!auth.isAdmin) return
|
if (!auth.isAdmin) return
|
||||||
router.push(`/admin/user/${user.id}`)
|
router.push(`/admin/user/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddClick = (event: Event) => {
|
const handleAddClick = (event: Event) => {
|
||||||
@@ -109,7 +75,18 @@ const handleAddClick = (event: Event) => {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const getRoleLabels = (roles?: string[]) => {
|
||||||
if (auth.isAdmin) reload()
|
if (!roles || roles.length === 0) {
|
||||||
|
return '---'
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles
|
||||||
|
.map((role) => roleLabelByValue.get(role) ?? role)
|
||||||
|
.join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!auth.isAdmin) return
|
||||||
|
userList.value = await getAdminUsers()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,60 +23,52 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-if="hasCaseId && auth.isAdmin"
|
v-if="hasCaseId"
|
||||||
:to="addBovineRoute"
|
:to="addBovineRoute"
|
||||||
class="inline-flex items-center justify-center text-xl text-white uppercase bg-primary-500 h-[50px] px-6 rounded hover:opacity-80 gap-2"
|
class="inline-flex items-center justify-center text-xl text-white uppercase bg-primary-500 h-[50px] px-6 rounded hover:opacity-80 gap-2"
|
||||||
|
:class="auth.isAdmin ? '' : 'cursor-not-allowed opacity-60 pointer-events-none'"
|
||||||
>
|
>
|
||||||
<Icon name="mdi:plus" size="28" />
|
<Icon name="mdi:plus" size="28" />
|
||||||
Ajouter
|
Ajouter
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 mb-16">
|
<div class="mt-8 border border-slate-200 mb-16">
|
||||||
<UiDataTable
|
<div
|
||||||
v-model:page="page"
|
class="grid grid-cols-3 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide"
|
||||||
v-model:per-page="perPage"
|
|
||||||
:columns="columns"
|
|
||||||
:items="items"
|
|
||||||
:total-items="totalItems"
|
|
||||||
:loading="loading"
|
|
||||||
:row-clickable="auth.isAdmin"
|
|
||||||
empty-message="Aucun bovin dans cette case."
|
|
||||||
@row-click="goToBovine"
|
|
||||||
>
|
>
|
||||||
<template #header-nationalNumber>
|
<div>Numéro national</div>
|
||||||
<UiTextInput
|
<div>Poids à l'arrivée (kg)</div>
|
||||||
v-model="filters.nationalNumber"
|
<div>Date d'arrivée</div>
|
||||||
placeholder="Numéro national"
|
</div>
|
||||||
size="compact"
|
<template v-if="bovines.length > 0">
|
||||||
/>
|
<div
|
||||||
</template>
|
v-for="bovine in bovines"
|
||||||
<template #header-receivedWeight>
|
:key="bovine.id"
|
||||||
<UiTextInput
|
class="grid grid-cols-3 gap-4 px-4 py-3 text-sm border-t border-slate-200"
|
||||||
v-model="filters.receivedWeight"
|
:class="auth.isAdmin ? 'cursor-pointer hover:bg-slate-50' : ''"
|
||||||
placeholder="Poids (kg)"
|
:role="auth.isAdmin ? 'button' : undefined"
|
||||||
size="compact"
|
:tabindex="auth.isAdmin ? 0 : undefined"
|
||||||
/>
|
@click="goToBovine(bovine.id)"
|
||||||
</template>
|
@keydown.enter="goToBovine(bovine.id)"
|
||||||
<template #header-arrivalDate>
|
>
|
||||||
<UiDateInput v-model="arrivalDateFilter" size="compact" />
|
<div>{{ bovine.nationalNumber }}</div>
|
||||||
</template>
|
<div>{{ bovine.receivedWeight ?? '—' }}</div>
|
||||||
<template #cell-arrivalDate="{ item }">
|
<div>{{ formatDate(bovine.arrivalDate) }}</div>
|
||||||
{{ formatDate(item.arrivalDate) }}
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell-receivedWeight="{ item }">
|
<div
|
||||||
{{ item.receivedWeight ?? '—' }}
|
v-else
|
||||||
</template>
|
class="px-4 py-3 text-sm border-t border-slate-200 text-slate-500"
|
||||||
</UiDataTable>
|
>
|
||||||
|
Aucun bovin dans cette case.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { BuildingCaseData } from '~/services/dto/building-case-data'
|
import type { BuildingCaseData } from '~/services/dto/building-case-data'
|
||||||
import type { BovineData } from '~/services/dto/bovine-data'
|
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import { useAuthStore } from '~/stores/auth'
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -88,44 +80,7 @@ const caseId = computed(() => Number(route.query.id))
|
|||||||
const hasCaseId = computed(() => Number.isFinite(caseId.value) && caseId.value > 0)
|
const hasCaseId = computed(() => Number.isFinite(caseId.value) && caseId.value > 0)
|
||||||
|
|
||||||
const buildingCase = ref<BuildingCaseData | null>(null)
|
const buildingCase = ref<BuildingCaseData | null>(null)
|
||||||
|
const bovines = computed(() => buildingCase.value?.bovines ?? [])
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
|
||||||
useDataTableServerState<BovineData>(
|
|
||||||
'bovines',
|
|
||||||
{
|
|
||||||
buildingCase: '',
|
|
||||||
nationalNumber: '',
|
|
||||||
receivedWeight: '',
|
|
||||||
'arrivalDate[after]': '',
|
|
||||||
'arrivalDate[strictly_before]': ''
|
|
||||||
},
|
|
||||||
{ initialPerPage: 10 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const addOneDay = (dateString: string): string => {
|
|
||||||
const [year, month, day] = dateString.split('-').map(Number)
|
|
||||||
const next = new Date(Date.UTC(year, month - 1, day + 1))
|
|
||||||
return next.toISOString().slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
const arrivalDateFilter = computed<string>({
|
|
||||||
get: () => (filters.value['arrivalDate[after]'] as string) ?? '',
|
|
||||||
set: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
filters.value['arrivalDate[after]'] = ''
|
|
||||||
filters.value['arrivalDate[strictly_before]'] = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value['arrivalDate[after]'] = value
|
|
||||||
filters.value['arrivalDate[strictly_before]'] = addOneDay(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'nationalNumber', label: 'Numéro national' },
|
|
||||||
{ key: 'receivedWeight', label: "Poids à l'arrivée (kg)" },
|
|
||||||
{ key: 'arrivalDate', label: "Date d'arrivée" }
|
|
||||||
]
|
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
if (!buildingCase.value) return ''
|
if (!buildingCase.value) return ''
|
||||||
@@ -159,27 +114,21 @@ const loadCase = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const printCaseReport = async () => {
|
const printCaseReport = async () => {
|
||||||
if (!hasCaseId.value) return
|
if (!hasCaseId.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const filename = `tableau_poids_case_${caseId.value}.pdf`
|
const filename = `tableau_poids_case_${caseId.value}.pdf`
|
||||||
await printPdf(`/building_cases/${caseId.value}/weights-report`, filename)
|
await printPdf(`/building_cases/${caseId.value}/weights-report`, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToBovine = (bovine: BovineData) => {
|
const goToBovine = (id: number) => {
|
||||||
if (!auth.isAdmin) return
|
if (!auth.isAdmin) return
|
||||||
router.push({
|
router.push({
|
||||||
path: '/infrastructure/bovine',
|
path: '/infrastructure/bovine',
|
||||||
query: { id: String(bovine.id), caseId: String(caseId.value) }
|
query: { id: String(id), caseId: String(caseId.value) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(caseId, (id) => {
|
watch(caseId, loadCase, { immediate: true })
|
||||||
if (!hasCaseId.value) {
|
|
||||||
filters.value.buildingCase = ''
|
|
||||||
buildingCase.value = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value.buildingCase = `/api/building_cases/${id}`
|
|
||||||
loadCase()
|
|
||||||
reload()
|
|
||||||
}, { immediate: true })
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,126 +5,41 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-[86px]">
|
<div class="px-[86px]">
|
||||||
<div class="mt-6 mb-16">
|
<div class="mt-6 border border-slate-200 mb-16 ">
|
||||||
<UiDataTable
|
<div class="grid grid-cols-6 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
v-model:page="page"
|
<div>Numéro</div>
|
||||||
v-model:per-page="perPage"
|
<div>Date et heure</div>
|
||||||
:columns="columns"
|
<div>Fournisseur</div>
|
||||||
:items="items"
|
<div>Adresse</div>
|
||||||
:total-items="totalItems"
|
<div>Type réception</div>
|
||||||
:loading="loading"
|
<div>Poids</div>
|
||||||
row-clickable
|
</div>
|
||||||
@row-click="goToReception"
|
<div
|
||||||
|
v-for="reception in receptionList"
|
||||||
|
:key="reception.id"
|
||||||
|
class="grid grid-cols-6 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
@click="goToReception(reception.id)"
|
||||||
>
|
>
|
||||||
<template #header-identificationNumber>
|
<div>{{ reception.identificationNumber}}</div>
|
||||||
<UiTextInput
|
<div>{{ formatDate(reception.receptionDate) }}</div>
|
||||||
v-model="filters.identificationNumber"
|
<div>{{ reception.supplier?.name }}</div>
|
||||||
placeholder="Numéro"
|
<div>{{ reception.address?.fullAddress }}</div>
|
||||||
size="compact"
|
<div>{{ reception.receptionType?.label }}</div>
|
||||||
/>
|
<div>{{ formatWeighing(reception) }}</div>
|
||||||
</template>
|
</div>
|
||||||
<template #header-receptionDate>
|
|
||||||
<UiDateInput
|
|
||||||
v-model="receptionDateFilter"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-supplier.name>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['supplier.name']"
|
|
||||||
placeholder="Fournisseur"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-address.fullAddress>
|
|
||||||
<UiTextInput
|
|
||||||
:model-value="''"
|
|
||||||
placeholder="Adresse"
|
|
||||||
size="compact"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-receptionType.label>
|
|
||||||
<UiSelect
|
|
||||||
v-model="filters['receptionType.id']"
|
|
||||||
placeholder="Type réception"
|
|
||||||
:options="receptionTypeOptions"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-weighing>
|
|
||||||
<UiTextInput
|
|
||||||
:model-value="''"
|
|
||||||
placeholder="Poids"
|
|
||||||
size="compact"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #cell-receptionDate="{ item }">
|
|
||||||
{{ formatDate(item.receptionDate) }}
|
|
||||||
</template>
|
|
||||||
<template #cell-weighing="{ item }">
|
|
||||||
{{ formatWeighing(item) }}
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ReceptionData } from '~/services/dto/reception-data'
|
import type {ReceptionData} from "~/services/dto/reception-data";
|
||||||
import type { ReceptionTypeData } from '~/services/dto/reception-type-data'
|
import {getReceptionList} from "~/services/reception";
|
||||||
import { getReceptionTypeList } from '~/services/reception-type'
|
import type {ShipmentData} from "~/services/dto/shipment-data";
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
|
||||||
|
|
||||||
|
const receptionList = ref<ReceptionData[]>()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const receptionTypes = ref<ReceptionTypeData[]>([])
|
|
||||||
|
|
||||||
const receptionTypeOptions = computed(() =>
|
|
||||||
receptionTypes.value.map(rt => ({ value: rt.id, label: rt.label }))
|
|
||||||
)
|
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
|
||||||
useDataTableServerState<ReceptionData>(
|
|
||||||
'receptions',
|
|
||||||
{
|
|
||||||
isValid: true,
|
|
||||||
'identificationNumber': '',
|
|
||||||
'supplier.name': '',
|
|
||||||
'receptionType.id': '',
|
|
||||||
'receptionDate[after]': '',
|
|
||||||
'receptionDate[strictly_before]': ''
|
|
||||||
},
|
|
||||||
{ initialPerPage: 10 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const addOneDay = (dateString: string): string => {
|
|
||||||
const [year, month, day] = dateString.split('-').map(Number)
|
|
||||||
const next = new Date(Date.UTC(year, month - 1, day + 1))
|
|
||||||
return next.toISOString().slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
const receptionDateFilter = computed<string>({
|
|
||||||
get: () => (filters.value['receptionDate[after]'] as string) ?? '',
|
|
||||||
set: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
filters.value['receptionDate[after]'] = ''
|
|
||||||
filters.value['receptionDate[strictly_before]'] = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value['receptionDate[after]'] = value
|
|
||||||
filters.value['receptionDate[strictly_before]'] = addOneDay(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'identificationNumber', label: 'Numéro', width: '75px' },
|
|
||||||
{ key: 'receptionDate', label: 'Date et heure', width: '120px' },
|
|
||||||
{ key: 'supplier.name', label: 'Fournisseur', width: '1.5fr' },
|
|
||||||
{ key: 'address.fullAddress', label: 'Adresse', width: '2fr' },
|
|
||||||
{ key: 'receptionType.label', label: 'Type réception', width: '0.9fr' },
|
|
||||||
{ key: 'weighing', label: 'Poids', width: '82px' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const formatDate = (date: string | null) => {
|
const formatDate = (date: string | null) => {
|
||||||
if (!date) return '—'
|
if (!date) return '—'
|
||||||
@@ -150,12 +65,11 @@ const formatWeighing = (reception: ReceptionData) => {
|
|||||||
return `${gross - tare} kg`
|
return `${gross - tare} kg`
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToReception = (reception: ReceptionData) => {
|
const goToReception = (id: number) => {
|
||||||
router.push(`/reception/update/${reception.id}`)
|
router.push(`/reception/update/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
receptionTypes.value = await getReceptionTypeList()
|
receptionList.value = await getReceptionList(true)
|
||||||
reload()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,135 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-start gap-10">
|
<WorkflowWaitingList
|
||||||
<Icon @click="router.push('/')" name="gg:arrow-left-o" size="44" class="cursor-pointer text-primary-500"/>
|
title="listes des réceptions en attente"
|
||||||
<h1 class="text-3xl font-bold uppercase text-primary-500">listes des réceptions en attente</h1>
|
:columns="columns"
|
||||||
</div>
|
:items="receptionList ?? []"
|
||||||
|
route-prefix="/reception"
|
||||||
<div class="px-[86px]">
|
:show-actions="auth.isAdmin"
|
||||||
<div class="mt-6 mb-16">
|
>
|
||||||
<UiDataTable
|
<template #cell-receptionDate="{ item }">
|
||||||
v-model:page="page"
|
{{ formatDate(item.receptionDate) }}
|
||||||
v-model:per-page="perPage"
|
</template>
|
||||||
:columns="columns"
|
<template #actions="{ item }">
|
||||||
:items="items"
|
<Icon
|
||||||
:total-items="totalItems"
|
name="mdi:delete-outline"
|
||||||
:loading="loading"
|
size="24"
|
||||||
:show-actions="auth.isAdmin"
|
class="cursor-pointer text-red-500 hover:text-red-700"
|
||||||
row-clickable
|
@click="confirmDelete(item)"
|
||||||
@row-click="goToReception"
|
/>
|
||||||
>
|
</template>
|
||||||
<template #header-receptionDate>
|
</WorkflowWaitingList>
|
||||||
<UiDateInput v-model="receptionDateFilter" size="compact" />
|
|
||||||
</template>
|
|
||||||
<template #header-supplier.name>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['supplier.name']"
|
|
||||||
placeholder="Fournisseur"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-address.fullAddress>
|
|
||||||
<UiTextInput :model-value="''" placeholder="Adresse" size="compact" disabled />
|
|
||||||
</template>
|
|
||||||
<template #header-receptionType.label>
|
|
||||||
<UiSelect
|
|
||||||
v-model="filters['receptionType.id']"
|
|
||||||
placeholder="Type réception"
|
|
||||||
:options="receptionTypeOptions"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-carrier.name>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['carrier.name']"
|
|
||||||
placeholder="Transporteur"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-licensePlate>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['licensePlate']"
|
|
||||||
placeholder="Immatriculation"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-actions>
|
|
||||||
<UiTextInput :model-value="''" placeholder="Actions" size="compact" disabled />
|
|
||||||
</template>
|
|
||||||
<template #cell-receptionDate="{ item }">
|
|
||||||
{{ formatDate(item.receptionDate) }}
|
|
||||||
</template>
|
|
||||||
<template #actions="{ item }">
|
|
||||||
<Icon
|
|
||||||
name="mdi:delete-outline"
|
|
||||||
size="24"
|
|
||||||
class="cursor-pointer text-red-500 hover:text-red-700"
|
|
||||||
@click="confirmDelete(item)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ReceptionData } from '~/services/dto/reception-data'
|
import type { ReceptionData } from '~/services/dto/reception-data'
|
||||||
import type { ReceptionTypeData } from '~/services/dto/reception-type-data'
|
import { getReceptionList, deleteReception } from '~/services/reception'
|
||||||
import { deleteReception } from '~/services/reception'
|
|
||||||
import { getReceptionTypeList } from '~/services/reception-type'
|
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import { useAuthStore } from '~/stores/auth'
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const receptionTypes = ref<ReceptionTypeData[]>([])
|
|
||||||
|
|
||||||
const receptionTypeOptions = computed(() =>
|
|
||||||
receptionTypes.value.map(rt => ({ value: rt.id, label: rt.label }))
|
|
||||||
)
|
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
|
||||||
useDataTableServerState<ReceptionData>(
|
|
||||||
'receptions',
|
|
||||||
{
|
|
||||||
isValid: false,
|
|
||||||
'supplier.name': '',
|
|
||||||
'carrier.name': '',
|
|
||||||
'licensePlate': '',
|
|
||||||
'receptionType.id': '',
|
|
||||||
'receptionDate[after]': '',
|
|
||||||
'receptionDate[strictly_before]': ''
|
|
||||||
},
|
|
||||||
{ initialPerPage: 10 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const addOneDay = (dateString: string): string => {
|
|
||||||
const [year, month, day] = dateString.split('-').map(Number)
|
|
||||||
const next = new Date(Date.UTC(year, month - 1, day + 1))
|
|
||||||
return next.toISOString().slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
const receptionDateFilter = computed<string>({
|
|
||||||
get: () => (filters.value['receptionDate[after]'] as string) ?? '',
|
|
||||||
set: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
filters.value['receptionDate[after]'] = ''
|
|
||||||
filters.value['receptionDate[strictly_before]'] = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value['receptionDate[after]'] = value
|
|
||||||
filters.value['receptionDate[strictly_before]'] = addOneDay(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'receptionDate', label: 'Date et heure', width: '120px' },
|
{ key: 'receptionDate', label: 'Date et heure' },
|
||||||
{ key: 'supplier.name', label: 'Fournisseur', width: '1.5fr' },
|
{ key: 'supplier.name', label: 'Fournisseur' },
|
||||||
{ key: 'address.fullAddress', label: 'Adresse', width: '2fr' },
|
{ key: 'address.fullAddress', label: 'Adresse' },
|
||||||
{ key: 'receptionType.label', label: 'Type réception', width: '1.1fr' },
|
{ key: 'receptionType.label', label: 'Type réception' },
|
||||||
{ key: 'carrier.name', label: 'Transporteur' },
|
{ key: 'carrier.name', label: 'Transporteur' },
|
||||||
{ key: 'licensePlate', label: 'Immatriculation', width: '110px' }
|
{ key: 'licensePlate', label: 'Immatriculation' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const receptionList = ref<ReceptionData[]>()
|
||||||
|
|
||||||
const formatDate = (date: string | null) => {
|
const formatDate = (date: string | null) => {
|
||||||
if (!date) return '—'
|
if (!date) return '—'
|
||||||
const d = new Date(date.replace(' ', 'T'))
|
const d = new Date(date.replace(' ', 'T'))
|
||||||
@@ -143,10 +51,6 @@ const formatDate = (date: string | null) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToReception = (reception: ReceptionData) => {
|
|
||||||
router.push(`/reception/${reception.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDelete = async (reception: ReceptionData) => {
|
const confirmDelete = async (reception: ReceptionData) => {
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
`Êtes-vous sûr de vouloir supprimer la réception ${reception.identificationNumber ?? `#${reception.id}`} ? Toutes les données liées seront supprimées.`
|
`Êtes-vous sûr de vouloir supprimer la réception ${reception.identificationNumber ?? `#${reception.id}`} ? Toutes les données liées seront supprimées.`
|
||||||
@@ -154,11 +58,10 @@ const confirmDelete = async (reception: ReceptionData) => {
|
|||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
||||||
await deleteReception(reception.id)
|
await deleteReception(reception.id)
|
||||||
reload()
|
receptionList.value = receptionList.value?.filter(r => r.id !== reception.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
receptionTypes.value = await getReceptionTypeList()
|
receptionList.value = await getReceptionList(false)
|
||||||
reload()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,148 +5,51 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-[86px]">
|
<div class="px-[86px]">
|
||||||
<div class="mt-6 mb-16">
|
<div class="mt-6 border border-slate-200 mb-16 ">
|
||||||
<UiDataTable
|
<div class="grid grid-cols-6 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
|
||||||
v-model:page="page"
|
<div>Numéro</div>
|
||||||
v-model:per-page="perPage"
|
<div>Date</div>
|
||||||
:columns="columns"
|
<div>Client</div>
|
||||||
:items="items"
|
<div>Adresse</div>
|
||||||
:total-items="totalItems"
|
<div>Type d'expéditon</div>
|
||||||
:loading="loading"
|
<div>Poids</div>
|
||||||
row-clickable
|
</div>
|
||||||
@row-click="goToShipment"
|
<div
|
||||||
|
v-for="shipment in shipmentList"
|
||||||
|
:key="shipment
|
||||||
|
.id"
|
||||||
|
class="grid grid-cols-6 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
@click="goShipment(shipment.id)"
|
||||||
>
|
>
|
||||||
<template #header-identificationNumber>
|
<div>{{ shipment.identificationNumber }}</div>
|
||||||
<UiTextInput
|
<div>{{ shipment.shipmentDate }}</div>
|
||||||
v-model="filters.identificationNumber"
|
<div>{{ shipment.customer?.name }}</div>
|
||||||
placeholder="Numéro"
|
<div>{{ shipment.address?.fullAddress }}</div>
|
||||||
size="compact"
|
<div>
|
||||||
/>
|
<template v-if="formatShipmentLines(shipment).length">
|
||||||
</template>
|
|
||||||
<template #header-shipmentDate>
|
|
||||||
<UiDateInput v-model="shipmentDateFilter" size="compact" />
|
|
||||||
</template>
|
|
||||||
<template #header-customer.name>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['customer.name']"
|
|
||||||
placeholder="Client"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-address.fullAddress>
|
|
||||||
<UiTextInput :model-value="''" placeholder="Adresse" size="compact" disabled />
|
|
||||||
</template>
|
|
||||||
<template #header-shipmentType.label>
|
|
||||||
<UiSelect
|
|
||||||
v-model="filters['shipmentType.id']"
|
|
||||||
placeholder="Type d'expédition"
|
|
||||||
:options="shipmentTypeOptions"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-weighing>
|
|
||||||
<UiTextInput :model-value="''" placeholder="Poids" size="compact" disabled />
|
|
||||||
</template>
|
|
||||||
<template #cell-shipmentDate="{ item }">
|
|
||||||
{{ formatDate(item.shipmentDate) }}
|
|
||||||
</template>
|
|
||||||
<template #cell-shipmentType.label="{ item }">
|
|
||||||
<template v-if="formatShipmentLines(item).length">
|
|
||||||
<div
|
<div
|
||||||
v-for="(line, index) in formatShipmentLines(item)"
|
v-for="(line, index) in formatShipmentLines(shipment)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="leading-5"
|
class="leading-5"
|
||||||
>
|
>
|
||||||
{{ line }}
|
{{ line }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>—</template>
|
</div>
|
||||||
</template>
|
<div>{{ formatWeighing(shipment) }}</div>
|
||||||
<template #cell-weighing="{ item }">
|
</div>
|
||||||
{{ formatWeighing(item) }}
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ShipmentData } from '~/services/dto/shipment-data'
|
import type {ShipmentData} from "~/services/dto/shipment-data";
|
||||||
import type { ShipmentTypeData } from '~/services/dto/shipment-type-data'
|
import {getShipmentList} from "~/services/shipment";
|
||||||
import { getShipmentTypeList } from '~/services/shipment-type'
|
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
|
||||||
|
|
||||||
|
const shipmentList = ref<ShipmentData[]>()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const shipmentTypes = ref<ShipmentTypeData[]>([])
|
|
||||||
|
|
||||||
const shipmentTypeOptions = computed(() =>
|
|
||||||
shipmentTypes.value.map(st => ({ value: st.id, label: st.label }))
|
|
||||||
)
|
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
|
||||||
useDataTableServerState<ShipmentData>(
|
|
||||||
'shipments',
|
|
||||||
{
|
|
||||||
isValid: true,
|
|
||||||
'identificationNumber': '',
|
|
||||||
'customer.name': '',
|
|
||||||
'shipmentType.id': '',
|
|
||||||
'shipmentDate[after]': '',
|
|
||||||
'shipmentDate[strictly_before]': ''
|
|
||||||
},
|
|
||||||
{ initialPerPage: 10 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const addOneDay = (dateString: string): string => {
|
|
||||||
const [year, month, day] = dateString.split('-').map(Number)
|
|
||||||
const next = new Date(Date.UTC(year, month - 1, day + 1))
|
|
||||||
return next.toISOString().slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
const shipmentDateFilter = computed<string>({
|
|
||||||
get: () => (filters.value['shipmentDate[after]'] as string) ?? '',
|
|
||||||
set: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
filters.value['shipmentDate[after]'] = ''
|
|
||||||
filters.value['shipmentDate[strictly_before]'] = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value['shipmentDate[after]'] = value
|
|
||||||
filters.value['shipmentDate[strictly_before]'] = addOneDay(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ key: 'identificationNumber', label: 'Numéro', width: '75px' },
|
|
||||||
{ key: 'shipmentDate', label: 'Date', width: '120px' },
|
|
||||||
{ key: 'customer.name', label: 'Client', width: '1.5fr' },
|
|
||||||
{ key: 'address.fullAddress', label: 'Adresse', width: '2fr' },
|
|
||||||
{ key: 'shipmentType.label', label: "Type d'expédition", width: '1.1fr' },
|
|
||||||
{ key: 'weighing', label: 'Poids', width: '82px' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const formatDate = (date: string | null) => {
|
|
||||||
if (!date) return '—'
|
|
||||||
const d = new Date(date.replace(' ', 'T'))
|
|
||||||
if (isNaN(d.getTime())) return date
|
|
||||||
return d.toLocaleDateString('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatShipmentLines = (shipment: ShipmentData) => {
|
|
||||||
if (!shipment.shipmentType && shipment.nbBovinSend == null) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const label = typeof shipment.shipmentType === 'string'
|
|
||||||
? shipment.shipmentType
|
|
||||||
: shipment.shipmentType?.label
|
|
||||||
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatWeighing = (shipment: ShipmentData) => {
|
const formatWeighing = (shipment: ShipmentData) => {
|
||||||
const gross = shipment.weights?.find((weight) => weight.type === 'gross')?.weight
|
const gross = shipment.weights?.find((weight) => weight.type === 'gross')?.weight
|
||||||
@@ -159,12 +62,24 @@ const formatWeighing = (shipment: ShipmentData) => {
|
|||||||
return `${gross - tare} kg`
|
return `${gross - tare} kg`
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToShipment = (shipment: ShipmentData) => {
|
|
||||||
router.push(`/shipment/update/${shipment.id}`)
|
const formatShipmentLines = (shipment: ShipmentData) => {
|
||||||
|
if (!shipment.shipmentType && shipment.nbBovinSend == null) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = typeof shipment.shipmentType === 'string'
|
||||||
|
? shipment.shipmentType
|
||||||
|
: shipment.shipmentType?.label
|
||||||
|
|
||||||
|
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
||||||
|
}
|
||||||
|
|
||||||
|
const goShipment = (id: number) => {
|
||||||
|
router.push(`/shipment/update/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
shipmentTypes.value = await getShipmentTypeList()
|
shipmentList.value = await getShipmentList(true)
|
||||||
reload()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,147 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-start gap-10">
|
<WorkflowWaitingList
|
||||||
<Icon @click="router.push('/')" name="gg:arrow-left-o" size="44" class="cursor-pointer text-primary-500"/>
|
title="listes des expéditions en attente"
|
||||||
<h1 class="text-3xl font-bold uppercase text-primary-500">listes des expéditions en attente</h1>
|
:columns="columns"
|
||||||
</div>
|
:items="shipmentList ?? []"
|
||||||
|
route-prefix="/shipment"
|
||||||
<div class="px-[86px]">
|
:show-actions="auth.isAdmin"
|
||||||
<div class="mt-6 mb-16">
|
>
|
||||||
<UiDataTable
|
<template #cell-shipmentDate="{ item }">
|
||||||
v-model:page="page"
|
{{ formatDate(item.shipmentDate) }}
|
||||||
v-model:per-page="perPage"
|
</template>
|
||||||
:columns="columns"
|
<template #cell-shipmentType="{ item }">
|
||||||
:items="items"
|
<template v-if="formatShipmentLines(item).length">
|
||||||
:total-items="totalItems"
|
<div
|
||||||
:loading="loading"
|
v-for="(line, index) in formatShipmentLines(item)"
|
||||||
:show-actions="auth.isAdmin"
|
:key="index"
|
||||||
row-clickable
|
class="leading-5"
|
||||||
@row-click="goToShipment"
|
>
|
||||||
>
|
{{ line }}
|
||||||
<template #header-shipmentDate>
|
</div>
|
||||||
<UiDateInput v-model="shipmentDateFilter" size="compact" />
|
</template>
|
||||||
</template>
|
<template v-else>—</template>
|
||||||
<template #header-customer.name>
|
</template>
|
||||||
<UiTextInput
|
<template #actions="{ item }">
|
||||||
v-model="filters['customer.name']"
|
<Icon
|
||||||
placeholder="Client"
|
name="mdi:delete-outline"
|
||||||
size="compact"
|
size="24"
|
||||||
/>
|
class="cursor-pointer text-red-500 hover:text-red-700"
|
||||||
</template>
|
@click="confirmDelete(item)"
|
||||||
<template #header-address.fullAddress>
|
/>
|
||||||
<UiTextInput :model-value="''" placeholder="Adresse" size="compact" disabled />
|
</template>
|
||||||
</template>
|
</WorkflowWaitingList>
|
||||||
<template #header-shipmentType.label>
|
|
||||||
<UiSelect
|
|
||||||
v-model="filters['shipmentType.id']"
|
|
||||||
placeholder="Type d'expé."
|
|
||||||
:options="shipmentTypeOptions"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-carrier.name>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['carrier.name']"
|
|
||||||
placeholder="Transporteur"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-licensePlate>
|
|
||||||
<UiTextInput
|
|
||||||
v-model="filters['licensePlate']"
|
|
||||||
placeholder="Immatriculation"
|
|
||||||
size="compact"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #header-actions>
|
|
||||||
<UiTextInput :model-value="''" placeholder="Actions" size="compact" disabled />
|
|
||||||
</template>
|
|
||||||
<template #cell-shipmentDate="{ item }">
|
|
||||||
{{ formatDate(item.shipmentDate) }}
|
|
||||||
</template>
|
|
||||||
<template #cell-shipmentType.label="{ item }">
|
|
||||||
<template v-if="formatShipmentLines(item).length">
|
|
||||||
<div
|
|
||||||
v-for="(line, index) in formatShipmentLines(item)"
|
|
||||||
:key="index"
|
|
||||||
class="leading-5"
|
|
||||||
>
|
|
||||||
{{ line }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>—</template>
|
|
||||||
</template>
|
|
||||||
<template #actions="{ item }">
|
|
||||||
<Icon
|
|
||||||
name="mdi:delete-outline"
|
|
||||||
size="24"
|
|
||||||
class="cursor-pointer text-red-500 hover:text-red-700"
|
|
||||||
@click="confirmDelete(item)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</UiDataTable>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ShipmentData } from '~/services/dto/shipment-data'
|
import type { ShipmentData } from '~/services/dto/shipment-data'
|
||||||
import type { ShipmentTypeData } from '~/services/dto/shipment-type-data'
|
import { getShipmentList, deleteShipment } from '~/services/shipment'
|
||||||
import { deleteShipment } from '~/services/shipment'
|
|
||||||
import { getShipmentTypeList } from '~/services/shipment-type'
|
|
||||||
import { useAuthStore } from '~/stores/auth'
|
import { useAuthStore } from '~/stores/auth'
|
||||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const shipmentTypes = ref<ShipmentTypeData[]>([])
|
|
||||||
|
|
||||||
const shipmentTypeOptions = computed(() =>
|
|
||||||
shipmentTypes.value.map(st => ({ value: st.id, label: st.label }))
|
|
||||||
)
|
|
||||||
|
|
||||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
|
||||||
useDataTableServerState<ShipmentData>(
|
|
||||||
'shipments',
|
|
||||||
{
|
|
||||||
isValid: false,
|
|
||||||
'customer.name': '',
|
|
||||||
'carrier.name': '',
|
|
||||||
'licensePlate': '',
|
|
||||||
'shipmentType.id': '',
|
|
||||||
'shipmentDate[after]': '',
|
|
||||||
'shipmentDate[strictly_before]': ''
|
|
||||||
},
|
|
||||||
{ initialPerPage: 10 }
|
|
||||||
)
|
|
||||||
|
|
||||||
const addOneDay = (dateString: string): string => {
|
|
||||||
const [year, month, day] = dateString.split('-').map(Number)
|
|
||||||
const next = new Date(Date.UTC(year, month - 1, day + 1))
|
|
||||||
return next.toISOString().slice(0, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
const shipmentDateFilter = computed<string>({
|
|
||||||
get: () => (filters.value['shipmentDate[after]'] as string) ?? '',
|
|
||||||
set: (value: string) => {
|
|
||||||
if (!value) {
|
|
||||||
filters.value['shipmentDate[after]'] = ''
|
|
||||||
filters.value['shipmentDate[strictly_before]'] = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filters.value['shipmentDate[after]'] = value
|
|
||||||
filters.value['shipmentDate[strictly_before]'] = addOneDay(value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'shipmentDate', label: 'Date et heure', width: '120px' },
|
{ key: 'shipmentDate', label: 'Date et heure' },
|
||||||
{ key: 'customer.name', label: 'Client', width: '1.5fr' },
|
{ key: 'customer.name', label: 'Client' },
|
||||||
{ key: 'address.fullAddress', label: 'Adresse', width: '2fr' },
|
{ key: 'address.fullAddress', label: 'Adresse' },
|
||||||
{ key: 'shipmentType.label', label: "Type d'expé.", width: '1.1fr' },
|
{ key: 'shipmentType', label: "Type d'expé." },
|
||||||
{ key: 'carrier.name', label: 'Transporteur' },
|
{ key: 'carrier.name', label: 'Transporteur' },
|
||||||
{ key: 'licensePlate', label: 'Immatriculation', width: '110px' }
|
{ key: 'licensePlate', label: 'Immatriculation' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const shipmentList = ref<ShipmentData[]>()
|
||||||
|
|
||||||
const formatDate = (date: string | null) => {
|
const formatDate = (date: string | null) => {
|
||||||
if (!date) return '—'
|
if (!date) return '—'
|
||||||
const d = new Date(date.replace(' ', 'T'))
|
const d = new Date(date.replace(' ', 'T'))
|
||||||
@@ -165,10 +73,6 @@ const formatShipmentLines = (shipment: ShipmentData) => {
|
|||||||
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
return [`${label ?? '—'} : ${shipment.nbBovinSend ?? '—'}`]
|
||||||
}
|
}
|
||||||
|
|
||||||
const goToShipment = (shipment: ShipmentData) => {
|
|
||||||
router.push(`/shipment/${shipment.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDelete = async (shipment: ShipmentData) => {
|
const confirmDelete = async (shipment: ShipmentData) => {
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
`Êtes-vous sûr de vouloir supprimer l'expédition ${shipment.identificationNumber ?? `#${shipment.id}`} ? Toutes les données liées seront supprimées.`
|
`Êtes-vous sûr de vouloir supprimer l'expédition ${shipment.identificationNumber ?? `#${shipment.id}`} ? Toutes les données liées seront supprimées.`
|
||||||
@@ -176,11 +80,10 @@ const confirmDelete = async (shipment: ShipmentData) => {
|
|||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
||||||
await deleteShipment(shipment.id)
|
await deleteShipment(shipment.id)
|
||||||
reload()
|
shipmentList.value = shipmentList.value?.filter(s => s.id !== shipment.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
shipmentTypes.value = await getShipmentTypeList()
|
shipmentList.value = await getShipmentList(false)
|
||||||
reload()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\ApiResource;
|
|
||||||
|
|
||||||
final class AnimalSummary
|
|
||||||
{
|
|
||||||
public ?string $countryCode = null;
|
|
||||||
public ?string $nationalNumber = null;
|
|
||||||
|
|
||||||
public ?string $sex = null;
|
|
||||||
public ?string $breedType = null;
|
|
||||||
public ?string $workNumber = null;
|
|
||||||
|
|
||||||
public ?string $birthDate = null;
|
|
||||||
public ?string $birthDateCompletenessFlag = null;
|
|
||||||
|
|
||||||
public ?bool $isFilie = null;
|
|
||||||
|
|
||||||
public ?string $motherNationalNumber = null;
|
|
||||||
public ?string $motherBreedType = null;
|
|
||||||
|
|
||||||
public ?string $fatherNationalNumber = null;
|
|
||||||
public ?string $fatherBreedType = null;
|
|
||||||
|
|
||||||
public ?string $birthExploitationNumber = null;
|
|
||||||
|
|
||||||
/** @var list<PresencePeriod> */
|
|
||||||
public array $presencePeriods = [];
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ namespace App\ApiResource;
|
|||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use App\State\System\AppVersionProvider;
|
use App\State\AppVersionProvider;
|
||||||
use Symfony\Component\Serializer\Attribute\Groups;
|
use Symfony\Component\Serializer\Attribute\Groups;
|
||||||
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ namespace App\ApiResource;
|
|||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
use App\State\BovinIdentificationProvider;
|
||||||
use App\State\Bovin\BovinIdentificationProvider;
|
|
||||||
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
uriTemplate: '/bovins/{numeroNational}/identification',
|
uriTemplate: '/bovins/{numeroNational}/identification',
|
||||||
openapi: new OpenApiOperation(tags: ['Bovins']),
|
|
||||||
provider: BovinIdentificationProvider::class
|
provider: BovinIdentificationProvider::class
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\ApiResource;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
|
||||||
use ApiPlatform\Metadata\Get;
|
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
|
||||||
use App\State\Bovin\BovinInventoryProvider;
|
|
||||||
|
|
||||||
#[ApiResource(
|
|
||||||
operations: [
|
|
||||||
new Get(
|
|
||||||
uriTemplate: '/bovins/inventory/{startDate}',
|
|
||||||
openapi: new OpenApiOperation(tags: ['Bovins']),
|
|
||||||
provider: BovinInventoryProvider::class
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
final class BovinInventory
|
|
||||||
{
|
|
||||||
#[ApiProperty(identifier: true)]
|
|
||||||
public string $startDate;
|
|
||||||
|
|
||||||
public ?string $endDate = null;
|
|
||||||
public ?bool $includesEarTagStock = null;
|
|
||||||
public int $nbBovins = 0;
|
|
||||||
public int $earTagSeriesCount = 0;
|
|
||||||
|
|
||||||
/** @var list<AnimalSummary> */
|
|
||||||
public array $animals = [];
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\ApiResource;
|
|
||||||
|
|
||||||
final class BovinPresumedExit
|
|
||||||
{
|
|
||||||
public ?string $countryCode = null;
|
|
||||||
public ?string $nationalNumber = null;
|
|
||||||
public ?string $exitDate = null;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\ApiResource;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
|
||||||
use ApiPlatform\Metadata\Get;
|
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
|
||||||
use App\State\Bovin\BovinPresumedExitsProvider;
|
|
||||||
|
|
||||||
#[ApiResource(
|
|
||||||
operations: [
|
|
||||||
new Get(
|
|
||||||
uriTemplate: '/bovins/presumed-exits',
|
|
||||||
openapi: new OpenApiOperation(tags: ['Bovins']),
|
|
||||||
provider: BovinPresumedExitsProvider::class
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
final class BovinPresumedExits
|
|
||||||
{
|
|
||||||
#[ApiProperty(identifier: true)]
|
|
||||||
public string $id = 'current';
|
|
||||||
|
|
||||||
public int $nbBovins = 0;
|
|
||||||
|
|
||||||
/** @var list<BovinPresumedExit> */
|
|
||||||
public array $presumedExits = [];
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\ApiResource;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
|
||||||
use ApiPlatform\Metadata\Get;
|
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
|
||||||
use App\State\Bovin\BovinReturnedDossiersProvider;
|
|
||||||
|
|
||||||
#[ApiResource(
|
|
||||||
operations: [
|
|
||||||
new Get(
|
|
||||||
uriTemplate: '/bovins/returned-dossiers/{startDate}',
|
|
||||||
openapi: new OpenApiOperation(tags: ['Bovins']),
|
|
||||||
provider: BovinReturnedDossiersProvider::class
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
final class BovinReturnedDossiers
|
|
||||||
{
|
|
||||||
#[ApiProperty(identifier: true)]
|
|
||||||
public string $startDate;
|
|
||||||
|
|
||||||
public int $nbBovins = 0;
|
|
||||||
|
|
||||||
/** @var list<AnimalSummary> */
|
|
||||||
public array $animals = [];
|
|
||||||
}
|
|
||||||
@@ -4,15 +4,12 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Patch;
|
use ApiPlatform\Metadata\Patch;
|
||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use App\State\Bovin\BovineProcessor;
|
use App\State\BovineProcessor;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Serializer\Attribute\Context;
|
use Symfony\Component\Serializer\Attribute\Context;
|
||||||
@@ -22,12 +19,6 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'bovine')]
|
#[ORM\Table(name: 'bovine')]
|
||||||
#[ORM\UniqueConstraint(name: 'uniq_bovine_national_number', columns: ['national_number'])]
|
#[ORM\UniqueConstraint(name: 'uniq_bovine_national_number', columns: ['national_number'])]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'nationalNumber' => 'ipartial',
|
|
||||||
'buildingCase' => 'exact',
|
|
||||||
'receivedWeight' => 'exact',
|
|
||||||
])]
|
|
||||||
#[ApiFilter(DateFilter::class, properties: ['arrivalDate'])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
@@ -15,10 +13,6 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
use Symfony\Component\Serializer\Attribute\Groups;
|
use Symfony\Component\Serializer\Attribute\Groups;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'label' => 'ipartial',
|
|
||||||
'code' => 'ipartial',
|
|
||||||
])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace App\Entity;
|
|||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
||||||
use App\State\Building\BuildingCaseWeightsReportProvider;
|
use App\State\BuildingCaseWeightsReportProvider;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
@@ -16,10 +14,6 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'carrier')]
|
#[ORM\Table(name: 'carrier')]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'name' => 'ipartial',
|
|
||||||
'code' => 'ipartial',
|
|
||||||
])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
@@ -19,12 +17,6 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'customer')]
|
#[ORM\Table(name: 'customer')]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'name' => 'ipartial',
|
|
||||||
'email' => 'ipartial',
|
|
||||||
'phone' => 'ipartial',
|
|
||||||
'createdBy.username' => 'ipartial',
|
|
||||||
])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
use ApiPlatform\Metadata\ApiFilter;
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
@@ -17,8 +15,8 @@ use ApiPlatform\Metadata\Patch;
|
|||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
||||||
use App\Dto\PontBasculeReading;
|
use App\Dto\PontBasculeReading;
|
||||||
use App\State\Reception\ReceptionReceiptProvider;
|
use App\State\ReceptionReceiptProvider;
|
||||||
use App\State\Reception\ReceptionWeighingProvider;
|
use App\State\ReceptionWeighingProvider;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
@@ -32,14 +30,6 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|||||||
#[ORM\HasLifecycleCallbacks]
|
#[ORM\HasLifecycleCallbacks]
|
||||||
#[ORM\Table(name: 'reception')]
|
#[ORM\Table(name: 'reception')]
|
||||||
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
|
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'identificationNumber' => 'ipartial',
|
|
||||||
'supplier.name' => 'ipartial',
|
|
||||||
'carrier.name' => 'ipartial',
|
|
||||||
'licensePlate' => 'ipartial',
|
|
||||||
'receptionType.id' => 'exact',
|
|
||||||
])]
|
|
||||||
#[ApiFilter(DateFilter::class, properties: ['receptionDate'])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
order: ['id' => 'DESC'],
|
order: ['id' => 'DESC'],
|
||||||
operations: [
|
operations: [
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
use ApiPlatform\Metadata\ApiFilter;
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
@@ -17,8 +15,8 @@ use ApiPlatform\Metadata\Patch;
|
|||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
|
||||||
use App\Dto\PontBasculeReading;
|
use App\Dto\PontBasculeReading;
|
||||||
use App\State\Shipment\ShipmentReceiptProvider;
|
use App\State\ShipmentReceiptProvider;
|
||||||
use App\State\Shipment\ShipmentWeighingProvider;
|
use App\State\ShipmentWeighingProvider;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
@@ -32,14 +30,6 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
|||||||
#[ORM\HasLifecycleCallbacks]
|
#[ORM\HasLifecycleCallbacks]
|
||||||
#[ORM\Table(name: 'shipment')]
|
#[ORM\Table(name: 'shipment')]
|
||||||
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
|
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'identificationNumber' => 'ipartial',
|
|
||||||
'customer.name' => 'ipartial',
|
|
||||||
'carrier.name' => 'ipartial',
|
|
||||||
'licensePlate' => 'ipartial',
|
|
||||||
'shipmentType.id' => 'exact',
|
|
||||||
])]
|
|
||||||
#[ApiFilter(DateFilter::class, properties: ['shipmentDate'])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
order: ['id' => 'DESC'],
|
order: ['id' => 'DESC'],
|
||||||
operations: [
|
operations: [
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
@@ -19,12 +17,6 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'supplier')]
|
#[ORM\Table(name: 'supplier')]
|
||||||
#[ApiFilter(SearchFilter::class, properties: [
|
|
||||||
'name' => 'ipartial',
|
|
||||||
'email' => 'ipartial',
|
|
||||||
'phone' => 'ipartial',
|
|
||||||
'createdBy.username' => 'ipartial',
|
|
||||||
])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
|
|||||||
@@ -4,17 +4,14 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Patch;
|
use ApiPlatform\Metadata\Patch;
|
||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use App\State\User\ActiveUsersProvider;
|
use App\State\ActiveUsersProvider;
|
||||||
use App\State\User\MeProvider;
|
use App\State\MeProvider;
|
||||||
use App\State\User\UserPasswordProcessor;
|
use App\State\UserPasswordProcessor;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
@@ -23,8 +20,6 @@ use Symfony\Component\Serializer\Attribute\SerializedName;
|
|||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'user', schema: 'public')]
|
#[ORM\Table(name: 'user', schema: 'public')]
|
||||||
#[ApiFilter(SearchFilter::class, properties: ['username' => 'ipartial'])]
|
|
||||||
#[ApiFilter(BooleanFilter::class, properties: ['isLocked'])]
|
|
||||||
#[ApiResource(
|
#[ApiResource(
|
||||||
operations: [
|
operations: [
|
||||||
new Get(
|
new Get(
|
||||||
@@ -58,8 +53,7 @@ use Symfony\Component\Serializer\Attribute\SerializedName;
|
|||||||
new GetCollection(
|
new GetCollection(
|
||||||
uriTemplate: '/admin/users',
|
uriTemplate: '/admin/users',
|
||||||
normalizationContext: ['groups' => ['user:read']],
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
security: "is_granted('ROLE_ADMIN')",
|
security: "is_granted('ROLE_ADMIN')"
|
||||||
paginationEnabled: true
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
normalizationContext: ['groups' => ['user:read']],
|
normalizationContext: ['groups' => ['user:read']],
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Service;
|
|
||||||
|
|
||||||
use App\ApiResource\AnimalSummary;
|
|
||||||
use App\ApiResource\PresencePeriod;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\AnimalSummaryDto;
|
|
||||||
|
|
||||||
final class AnimalSummaryMapper
|
|
||||||
{
|
|
||||||
public function map(AnimalSummaryDto $dto): AnimalSummary
|
|
||||||
{
|
|
||||||
$summary = new AnimalSummary();
|
|
||||||
|
|
||||||
$identification = $dto->identification;
|
|
||||||
|
|
||||||
$summary->countryCode = $identification?->bovin?->countryCode;
|
|
||||||
$summary->nationalNumber = $identification?->bovin?->nationalNumber;
|
|
||||||
|
|
||||||
$summary->sex = $identification?->sex;
|
|
||||||
$summary->breedType = $identification?->breedType;
|
|
||||||
$summary->workNumber = $identification?->workNumber;
|
|
||||||
|
|
||||||
$summary->birthDate = $identification?->birthDate?->date?->format('Y-m-d');
|
|
||||||
$summary->birthDateCompletenessFlag = $identification?->birthDate?->completenessFlag;
|
|
||||||
|
|
||||||
$summary->isFilie = $identification?->isFilie;
|
|
||||||
|
|
||||||
$summary->motherNationalNumber = $identification?->motherCarrier?->bovin?->nationalNumber;
|
|
||||||
$summary->motherBreedType = $identification?->motherCarrier?->breedType;
|
|
||||||
|
|
||||||
$summary->fatherNationalNumber = $identification?->fatherIpg?->bovin?->nationalNumber;
|
|
||||||
$summary->fatherBreedType = $identification?->fatherIpg?->breedType;
|
|
||||||
|
|
||||||
$summary->birthExploitationNumber = $identification?->birthExploitation?->exploitationNumber;
|
|
||||||
|
|
||||||
foreach ($dto->presencePeriods as $presencePeriodDto) {
|
|
||||||
$presencePeriod = new PresencePeriod();
|
|
||||||
|
|
||||||
$presencePeriod->entryDate = $presencePeriodDto->entry?->date?->format('Y-m-d');
|
|
||||||
$presencePeriod->entryCause = $presencePeriodDto->entry?->cause;
|
|
||||||
|
|
||||||
$presencePeriod->exitDate = $presencePeriodDto->exit?->date?->format('Y-m-d');
|
|
||||||
$presencePeriod->exitCause = $presencePeriodDto->exit?->cause;
|
|
||||||
|
|
||||||
$summary->presencePeriods[] = $presencePeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $summary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\User;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\System;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\State\Bovin;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
|
||||||
use ApiPlatform\State\ProviderInterface;
|
|
||||||
use App\ApiResource\BovinInventory;
|
|
||||||
use App\Service\AnimalSummaryMapper;
|
|
||||||
use DateTimeImmutable;
|
|
||||||
use Exception;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
|
||||||
|
|
||||||
use function count;
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implements ProviderInterface<null|BovinInventory>
|
|
||||||
*/
|
|
||||||
final class BovinInventoryProvider implements ProviderInterface
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private BovinApiInterface $bovinApi,
|
|
||||||
private RequestStack $requestStack,
|
|
||||||
private AnimalSummaryMapper $animalSummaryMapper,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?BovinInventory
|
|
||||||
{
|
|
||||||
$startDateRaw = (string) ($uriVariables['startDate'] ?? '');
|
|
||||||
if ('' === $startDateRaw) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$startDate = new DateTimeImmutable($startDateRaw);
|
|
||||||
} catch (Exception) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
|
||||||
|
|
||||||
$endDate = null;
|
|
||||||
$endDateRaw = $request?->query->get('endDate');
|
|
||||||
if (is_string($endDateRaw) && '' !== $endDateRaw) {
|
|
||||||
try {
|
|
||||||
$endDate = new DateTimeImmutable($endDateRaw);
|
|
||||||
} catch (Exception) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$includeEarTagStock = (bool) $request?->query->getBoolean('includeEarTagStock', false);
|
|
||||||
|
|
||||||
$inventoryDto = $this->bovinApi->getInventory(
|
|
||||||
startDate: $startDate,
|
|
||||||
endDate: $endDate,
|
|
||||||
includeEarTagStock: $includeEarTagStock,
|
|
||||||
);
|
|
||||||
|
|
||||||
$resource = new BovinInventory();
|
|
||||||
$resource->startDate = $inventoryDto->startDate?->format('Y-m-d') ?? $startDate->format('Y-m-d');
|
|
||||||
$resource->endDate = $inventoryDto->endDate?->format('Y-m-d');
|
|
||||||
$resource->includesEarTagStock = $inventoryDto->includesEarTagStock;
|
|
||||||
$resource->nbBovins = $inventoryDto->nbBovins;
|
|
||||||
$resource->earTagSeriesCount = count($inventoryDto->earTagSeries);
|
|
||||||
|
|
||||||
foreach ($inventoryDto->animals as $animalDto) {
|
|
||||||
$resource->animals[] = $this->animalSummaryMapper->map($animalDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\State\Bovin;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
|
||||||
use ApiPlatform\State\ProviderInterface;
|
|
||||||
use App\ApiResource\BovinPresumedExit;
|
|
||||||
use App\ApiResource\BovinPresumedExits;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implements ProviderInterface<BovinPresumedExits>
|
|
||||||
*/
|
|
||||||
final class BovinPresumedExitsProvider implements ProviderInterface
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private BovinApiInterface $bovinApi,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): BovinPresumedExits
|
|
||||||
{
|
|
||||||
$dto = $this->bovinApi->getPresumedExits();
|
|
||||||
|
|
||||||
$resource = new BovinPresumedExits();
|
|
||||||
$resource->nbBovins = $dto->nbBovins;
|
|
||||||
|
|
||||||
foreach ($dto->presumedExits as $exitDto) {
|
|
||||||
$exit = new BovinPresumedExit();
|
|
||||||
$exit->countryCode = $exitDto->bovin?->countryCode;
|
|
||||||
$exit->nationalNumber = $exitDto->bovin?->nationalNumber;
|
|
||||||
$exit->exitDate = $exitDto->exitDate?->format('Y-m-d');
|
|
||||||
|
|
||||||
$resource->presumedExits[] = $exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\State\Bovin;
|
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
|
||||||
use ApiPlatform\State\ProviderInterface;
|
|
||||||
use App\ApiResource\BovinReturnedDossiers;
|
|
||||||
use App\Service\AnimalSummaryMapper;
|
|
||||||
use DateTimeImmutable;
|
|
||||||
use Exception;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implements ProviderInterface<null|BovinReturnedDossiers>
|
|
||||||
*/
|
|
||||||
final class BovinReturnedDossiersProvider implements ProviderInterface
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private BovinApiInterface $bovinApi,
|
|
||||||
private AnimalSummaryMapper $animalSummaryMapper,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?BovinReturnedDossiers
|
|
||||||
{
|
|
||||||
$startDateRaw = (string) ($uriVariables['startDate'] ?? '');
|
|
||||||
if ('' === $startDateRaw) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$startDate = new DateTimeImmutable($startDateRaw);
|
|
||||||
} catch (Exception) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dto = $this->bovinApi->getReturnedDossiers($startDate);
|
|
||||||
|
|
||||||
$resource = new BovinReturnedDossiers();
|
|
||||||
$resource->startDate = $dto->startDate?->format('Y-m-d') ?? $startDate->format('Y-m-d');
|
|
||||||
$resource->nbBovins = $dto->nbBovins;
|
|
||||||
|
|
||||||
foreach ($dto->animals as $animalDto) {
|
|
||||||
$resource->animals[] = $this->animalSummaryMapper->map($animalDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Bovin;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Bovin;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProcessorInterface;
|
use ApiPlatform\State\ProcessorInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Building;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\User;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Reception;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Reception;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Shipment;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\Shipment;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProviderInterface;
|
use ApiPlatform\State\ProviderInterface;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\State\User;
|
namespace App\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProcessorInterface;
|
use ApiPlatform\State\ProcessorInterface;
|
||||||
@@ -284,9 +284,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="days">Foin</th>
|
<th colspan="{{ monthHeaders|length }}" class="sub-title">POIDS PAR MOIS</th>
|
||||||
<th class="days">Foin</th>
|
|
||||||
<th colspan="{{ monthHeaders|length -2 }}" class="sub-title">POIDS PAR MOIS</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Tests\State\Reception;
|
namespace App\Tests\State;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use App\Dto\PontBasculeReading;
|
use App\Dto\PontBasculeReading;
|
||||||
use App\Service\PontBasculePayloadDecoder;
|
use App\Service\PontBasculePayloadDecoder;
|
||||||
use App\Service\PontBasculeService;
|
use App\Service\PontBasculeService;
|
||||||
use App\State\Reception\ReceptionWeighingProvider;
|
use App\State\ReceptionWeighingProvider;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||||
Reference in New Issue
Block a user