first commit
This commit is contained in:
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
7
.htaccess
Normal file
7
.htaccess
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
# If an existing asset or directory is requested go to it as it is
|
||||||
|
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
||||||
|
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
||||||
|
RewriteRule ^ - [L]
|
||||||
|
# If the requested resource doesn't exist, use index.html
|
||||||
|
RewriteRule ^ ./index.html
|
||||||
4
.vscode/extensions.json
vendored
Normal file
4
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||||
|
"recommendations": ["angular.ng-template"]
|
||||||
|
}
|
||||||
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ng serve",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: start",
|
||||||
|
"url": "http://localhost:4200/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng test",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "npm: test",
|
||||||
|
"url": "http://localhost:9876/debug.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
42
.vscode/tasks.json
vendored
Normal file
42
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "start",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "test",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"pattern": "$tsc",
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": {
|
||||||
|
"regexp": "(.*?)"
|
||||||
|
},
|
||||||
|
"endsPattern": {
|
||||||
|
"regexp": "bundle generation complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
FROM nginx:latest as build
|
||||||
|
|
||||||
|
## Replace the default nginx index page with our Angular app
|
||||||
|
COPY ./.htaccess /usr/share/nginx/html
|
||||||
|
|
||||||
|
COPY dist/infocad-back-office /usr/share/nginx/html
|
||||||
|
|
||||||
|
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
RUN chmod -R 777 /usr/share/nginx/html
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "-c", \
|
||||||
|
"echo API_URL=[$API_URL], && \
|
||||||
|
sed -i s#MY_APP_API_URL#$API_URL#g /usr/share/nginx/html/main.*.js && \
|
||||||
|
nginx -g 'daemon off;'"]
|
||||||
|
|
||||||
|
#from my MAC
|
||||||
|
#docker build --platform linux/amd64 -t front-fiscad . ou docker build -t front-fiscad .
|
||||||
|
#docker save -o ./front-fiscad.tar front-fiscad
|
||||||
|
|
||||||
|
#docker load -i front-fiscad.tar
|
||||||
|
#docker run -d -p 8081:80 -e API_URL=http://localhost:9090/ front-fiscad
|
||||||
|
#docker ps
|
||||||
|
#docker stop CONTAINER_ID ==> docker ps (pour trouver le CONTAINER_ID)
|
||||||
|
#docker images -a
|
||||||
|
#docker rmi IMAGE_ID (supprimer une image) ==> docker ps (pour trouver le IMAGE_ID)
|
||||||
27
README.md
Normal file
27
README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# InfocadBackOffice
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.0.0.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||||
121
angular.json
Normal file
121
angular.json
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"infocad-back-office": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/infocad-back-office",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets",
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
|
||||||
|
"output": "/assets/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/theme.less",
|
||||||
|
"src/styles.css",
|
||||||
|
"src/assets/static/vendors/iconfonts/mdi/css/materialdesignicons.min.css",
|
||||||
|
"src/assets/static/vendors/css/vendor.bundle.base.css",
|
||||||
|
"src/assets/static/css/style.css",
|
||||||
|
"node_modules/datatables.net-bs4/css/dataTables.bootstrap4.min.css",
|
||||||
|
"node_modules/datatables.net-responsive-dt/css/responsive.dataTables.css"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"src/assets/static/vendors/js/vendor.bundle.base.js",
|
||||||
|
"src/assets/static/vendors/js/vendor.bundle.addons.js",
|
||||||
|
"src/assets/static/js/off-canvas.js",
|
||||||
|
"src/assets/static/js/misc.js",
|
||||||
|
"node_modules/datatables.net/js/jquery.dataTables.min.js",
|
||||||
|
"node_modules/datatables.net-responsive/js/dataTables.responsive.min.js",
|
||||||
|
"node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js",
|
||||||
|
"node_modules/datatables.net-responsive-bs4/js/responsive.bootstrap4.min.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "3mb",
|
||||||
|
"maximumError": "6mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "6mb",
|
||||||
|
"maximumError": "9mb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "infocad-back-office:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "infocad-back-office:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "infocad-back-office:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js",
|
||||||
|
"zone.js/testing"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.css"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
front-fiscad.tar
Normal file
BIN
front-fiscad.tar
Normal file
Binary file not shown.
22
nginx.conf
Normal file
22
nginx.conf
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name mysite.com www.mysite.com;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri$args $uri$args/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
note.txt
Normal file
8
note.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
- AGENT DE CONSTATATION => sont des agents qui interviennent dans les secteurs découpages
|
||||||
|
- INSPECTEUR (SONT DES CHEFS SECTEURS) => peuvent être chef pour plusieurs secteurs
|
||||||
|
|
||||||
|
- notion de section qui est nouveau, Structure -> Section -> secteur -> secteur découpages
|
||||||
|
- Dans une fonction, renseigner obligatoirement la structure, la section et le secteur
|
||||||
|
afin de régler le problème des profils de management
|
||||||
|
- Dans structure, il peut ne pas y avoir le choix de la commune, donc dans l'enregistrement de
|
||||||
|
secteur, il faut renseigner les découpages administratives sans tenir de commune qui est dans structure ou faire le filtre.
|
||||||
16985
package-lock.json
generated
Normal file
16985
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
100
package.json
Normal file
100
package.json
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"name": "infocad-back-office",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^16.0.0",
|
||||||
|
"@angular/common": "^16.0.0",
|
||||||
|
"@angular/compiler": "^16.0.0",
|
||||||
|
"@angular/core": "^16.0.0",
|
||||||
|
"@angular/forms": "^16.0.0",
|
||||||
|
"@angular/platform-browser": "^16.0.0",
|
||||||
|
"@angular/platform-browser-dynamic": "^16.0.0",
|
||||||
|
"@angular/router": "^16.0.0",
|
||||||
|
"@auth0/angular-jwt": "^5.2.0",
|
||||||
|
"@capacitor-community/file-opener": "^8.0.0",
|
||||||
|
"@capacitor-community/http": "^1.4.1",
|
||||||
|
"@capacitor-community/sqlite": "^7.0.3",
|
||||||
|
"@capacitor/angular": "^2.0.3",
|
||||||
|
"@capacitor/app": "^8.0.0",
|
||||||
|
"@capacitor/camera": "^6.0.2",
|
||||||
|
"@capacitor/core": "^8.0.1",
|
||||||
|
"@capacitor/device": "^8.0.0",
|
||||||
|
"@capacitor/filesystem": "^8.0.0",
|
||||||
|
"@capacitor/geolocation": "^8.0.0",
|
||||||
|
"@capacitor/haptics": "^8.0.0",
|
||||||
|
"@capacitor/keyboard": "^8.0.0",
|
||||||
|
"@capacitor/network": "^8.0.0",
|
||||||
|
"@capawesome/capacitor-file-picker": "^8.0.0",
|
||||||
|
"@ionic/angular": "^8.7.17",
|
||||||
|
"@ionic/pwa-elements": "^3.3.0",
|
||||||
|
"@tanstack/table-core": "^8.21.3",
|
||||||
|
"@types/geojson": "^7946.0.16",
|
||||||
|
"@types/node": "^22.1.0",
|
||||||
|
"@types/ol": "^7.0.0",
|
||||||
|
"ag-grid-angular": "^32.3.9",
|
||||||
|
"ag-grid-community": "^32.3.9",
|
||||||
|
"angular-datatables": "^16.0.1",
|
||||||
|
"apexcharts": "^3.54.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"capacitor-blob-writer": "^1.1.19",
|
||||||
|
"chart.js": "^4.5.1",
|
||||||
|
"copyfiles": "^2.4.1",
|
||||||
|
"cordova-plugin-file": "^8.1.3",
|
||||||
|
"cordova-plugin-zeep": "^0.0.5",
|
||||||
|
"cordova-plugin-zip": "^3.1.0",
|
||||||
|
"datatables.net": "^1.13.1",
|
||||||
|
"datatables.net-bs4": "^1.10.20",
|
||||||
|
"datatables.net-dt": "^1.13.1",
|
||||||
|
"datatables.net-responsive-bs4": "^2.2.3",
|
||||||
|
"datatables.net-responsive-dt": "^2.4.0",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
|
"install": "^0.13.0",
|
||||||
|
"ionicons": "^8.0.13",
|
||||||
|
"jeep-sqlite": "^2.8.0",
|
||||||
|
"jquery": "^3.6.1",
|
||||||
|
"jsonfile": "^6.2.0",
|
||||||
|
"jspdf": "^4.0.0",
|
||||||
|
"jszip": "^3.10.1",
|
||||||
|
"ng-apexcharts": "^1.12.0",
|
||||||
|
"ng-zorro-antd": "^16.2.2",
|
||||||
|
"ng2-charts": "^8.0.0",
|
||||||
|
"ngx-image-compress": "^18.1.5",
|
||||||
|
"npm": "^11.8.0",
|
||||||
|
"ol": "^10.4.0",
|
||||||
|
"ol-layerswitcher": "^4.1.2",
|
||||||
|
"pako": "^2.1.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"sql.js": "^1.13.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"type-is": "^2.0.1",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"zone.js": "~0.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^16.0.0",
|
||||||
|
"@angular/cli": "~16.0.0",
|
||||||
|
"@angular/compiler-cli": "^16.0.0",
|
||||||
|
"@types/datatables.net": "^1.10.24",
|
||||||
|
"@types/file-saver": "^2.0.7",
|
||||||
|
"@types/jasmine": "~4.3.0",
|
||||||
|
"@types/jquery": "^3.5.14",
|
||||||
|
"css-loader": "^7.1.2",
|
||||||
|
"jasmine-core": "~4.6.0",
|
||||||
|
"karma": "~6.4.0",
|
||||||
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
"karma-coverage": "~2.2.0",
|
||||||
|
"karma-jasmine": "~5.1.0",
|
||||||
|
"karma-jasmine-html-reporter": "~2.0.0",
|
||||||
|
"mini-css-extract-plugin": "^2.9.2",
|
||||||
|
"style-loader": "^4.0.0",
|
||||||
|
"typescript": "~5.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/app/app-routing.module.ts
Normal file
14
src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', loadChildren: () => import('./home/home.module').then(h => h.HomeModule) },
|
||||||
|
{ path: 'principale', loadChildren: () => import('./manage/manage.module').then(m => m.ManageModule) },
|
||||||
|
{ path: 'core', loadChildren: () => import('./office/office.module').then(o => o.OfficeModule) },
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AppRoutingModule { }
|
||||||
0
src/app/app.component.css
Normal file
0
src/app/app.component.css
Normal file
11
src/app/app.component.html
Normal file
11
src/app/app.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!-- NG-ZORRO -->
|
||||||
|
|
||||||
|
<div *ngIf="isActionInProgress" class="loading-spin">
|
||||||
|
<div aria-valuemax="100" aria-valuemin="0" class="bp4-spinner bp4-intent-primary" role="progressbar">
|
||||||
|
<div class="bp4-spinner-animation">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
|
||||||
35
src/app/app.component.ts
Normal file
35
src/app/app.component.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Component, ChangeDetectorRef } from '@angular/core';
|
||||||
|
import { GlobalService } from './global.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
|
||||||
|
isActionInProgress = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private cdref: ChangeDetectorRef,
|
||||||
|
private router: Router
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterContentChecked() {
|
||||||
|
this.cdref.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
78
src/app/app.module.ts
Normal file
78
src/app/app.module.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { NZ_I18N } from 'ng-zorro-antd/i18n';
|
||||||
|
import { fr_FR } from 'ng-zorro-antd/i18n';
|
||||||
|
import { registerLocaleData } from '@angular/common';
|
||||||
|
import fr from '@angular/common/locales/fr';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { TokenStorage } from './utilitaire/token-storage';
|
||||||
|
import { NzConfig, provideNzConfig } from 'ng-zorro-antd/core/config';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { ErrorInterceptor } from './utilitaire/error-interceptor';
|
||||||
|
import { JwtInterceptor } from './utilitaire/jwt-interceptor';
|
||||||
|
import { SQLiteService } from './services/sqlite.service';
|
||||||
|
import { DetailService } from './services/detail.service';
|
||||||
|
import { DatabaseService } from './services/database.service';
|
||||||
|
import { InitializeAppService } from './services/initialize.app.service';
|
||||||
|
import { MigrationService } from './services/migrations.service';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
|
||||||
|
registerLocaleData(fr);
|
||||||
|
|
||||||
|
const ngZorroConfig: NzConfig = {
|
||||||
|
message: { nzTop: 80 },
|
||||||
|
notification: { nzTop: 240 }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function initializeFactory(init: InitializeAppService) {
|
||||||
|
return () => init.initializeApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
HttpClientModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: NZ_I18N, useValue: fr_FR },
|
||||||
|
TokenStorage,
|
||||||
|
JwtHelperService,
|
||||||
|
{provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true},
|
||||||
|
{provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true},
|
||||||
|
provideNzConfig(ngZorroConfig),
|
||||||
|
|
||||||
|
SQLiteService,
|
||||||
|
DetailService,
|
||||||
|
|
||||||
|
DatabaseService,
|
||||||
|
|
||||||
|
InitializeAppService,
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useFactory: initializeFactory,
|
||||||
|
deps: [InitializeAppService],
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
|
||||||
|
MigrationService,
|
||||||
|
|
||||||
|
NzModalService,
|
||||||
|
],
|
||||||
|
bootstrap: [AppComponent],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
||||||
102
src/app/camera.service.ts
Normal file
102
src/app/camera.service.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Camera, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
|
||||||
|
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
||||||
|
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
||||||
|
import * as pako from 'pako';
|
||||||
|
import { FileService } from './services/file.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CameraService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fileService: FileService
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async takePhoto(): Promise<any> {
|
||||||
|
const photo = await Camera.getPhoto({
|
||||||
|
//resultType: CameraResultType.Uri,
|
||||||
|
resultType: CameraResultType.Base64,
|
||||||
|
source: CameraSource.Camera,
|
||||||
|
quality: 100,
|
||||||
|
//width : 800,
|
||||||
|
//height : 600,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileName = new Date().getTime() + '.'+ photo.format;
|
||||||
|
const filePath = 'InfoRFU_camera_' + fileName;
|
||||||
|
|
||||||
|
const resp = {
|
||||||
|
name: filePath,
|
||||||
|
filepath: 'image/jpeg',
|
||||||
|
webviewPath: photo.webPath,
|
||||||
|
blobData: photo.base64String ? photo.base64String.replace('data:image/jpeg;base64', ''): '',
|
||||||
|
base64Data: 'data:image/jpeg;base64,'+ (photo.base64String ? photo.base64String : '') //taille 512 Ko
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.fileService.createFolder();
|
||||||
|
await this.fileService.write(resp.base64Data, resp.name);
|
||||||
|
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
async choisirFichier(): Promise<any> {
|
||||||
|
const result = await FilePicker.pickFiles({
|
||||||
|
types: ['application/pdf', 'image/*'],
|
||||||
|
limit: 1,
|
||||||
|
readData: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resp = {
|
||||||
|
name: 'InfoRfu_disque_' +(new Date().getTime()) + '_' + result.files[0].name.slice(-6),
|
||||||
|
filepath: result.files[0].mimeType,
|
||||||
|
base64Data: 'data:'+result.files[0].mimeType+';base64,'+result.files[0].data,
|
||||||
|
blobData: result.files[0].data
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.fileService.createFolder();
|
||||||
|
await this.fileService.write(resp.base64Data, resp.name);
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
compressData(data: string): string {
|
||||||
|
const compressed = pako.deflate(data, {
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
return btoa(String.fromCharCode(...compressed)); // Encode en base64
|
||||||
|
}
|
||||||
|
|
||||||
|
async base64FromPath(path: string): Promise<string> {
|
||||||
|
const response = await fetch(path);
|
||||||
|
const blob = await response.blob();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onerror = reject;
|
||||||
|
reader.onload = () => {
|
||||||
|
if (typeof reader.result === 'string') {
|
||||||
|
resolve(reader.result);
|
||||||
|
} else {
|
||||||
|
reject('method did not return a string');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileBse64FromPath(filePath: string): Promise<string> {
|
||||||
|
const file = await Filesystem.readFile({
|
||||||
|
path: filePath,
|
||||||
|
directory: Directory.Data
|
||||||
|
});
|
||||||
|
|
||||||
|
const imageUrl = 'data:image/jpeg;base64,' + file.data;
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
173
src/app/crud.service.ts
Normal file
173
src/app/crud.service.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CrudService {
|
||||||
|
|
||||||
|
url: string = environment.backend;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient
|
||||||
|
) { }
|
||||||
|
|
||||||
|
getAll(paramURL: string): Observable<any[]> {
|
||||||
|
return this.http.get<any>(`${this.url}/${paramURL}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(paramURL: string, data: any): Observable<any> {
|
||||||
|
return this.http.post<any>(`${this.url}/${paramURL}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByPost(paramURL: string, data: any): Observable<any> {
|
||||||
|
return this.http.post<any>(`${this.url}/${paramURL}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(paramURL: string, data: any): Observable<any> {
|
||||||
|
return this.http.put<any>(`${this.url}/${paramURL}/${data.id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateWithoutId(paramURL: string, data: any): Observable<any> {
|
||||||
|
return this.http.put<any>(`${this.url}/${paramURL}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteElement(paramURL: string, id: number): Observable<any> {
|
||||||
|
return this.http.delete<any>(`${this.url}/${paramURL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
doGetAction(paramURL: string, id: number): Observable<any> {
|
||||||
|
return this.http.get<any>(`${this.url}/${paramURL}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getById(paramURL: string, id: number): Observable<Object> {
|
||||||
|
return this.http.get(`${this.url}/${paramURL}/id/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rapportBlocListByStructure(type: string, structureId: number): Observable<any> {
|
||||||
|
return this.http.get(`${this.url}/rapport/structure/blocs/${type}/${structureId}`, { responseType: 'blob' });
|
||||||
|
}
|
||||||
|
|
||||||
|
rapportGetEnqueteListByBloc(type: string, blocId: number): Observable<any> {
|
||||||
|
return this.http.get(`${this.url}/rapport/bloc/enquetes/${type}/${blocId}`, { responseType: 'blob' });
|
||||||
|
}
|
||||||
|
|
||||||
|
rapportPostEnqueteListByBloc(type: string, requestFiltre: any): Observable<any> {
|
||||||
|
return this.http.post(`${this.url}/rapport/filtre/enquetes/${type}`, requestFiltre, { responseType: 'blob' });
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition(): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
navigator.geolocation.getCurrentPosition(resp => {
|
||||||
|
resolve({ lng: resp.coords.longitude, lat: resp.coords.latitude });
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile(payload: any, upload: any): Observable<Object> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('Content-Type', 'multipart/form-data');
|
||||||
|
formData.append('file', upload as File);
|
||||||
|
/*formData.append('idTypePiece', payload.idTypePiece as string);
|
||||||
|
formData.append('reference', payload.reference as string);
|
||||||
|
formData.append('dateEtablissement', payload.dateEtablissement as string);
|
||||||
|
formData.append('dateExpiration', payload.dateExpiration as string);*/
|
||||||
|
|
||||||
|
return this.http.post(`${this.url}/parcelle-geom/create-from-geojsonfile?reference=${payload.reference}&description=${payload.description}`, formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataTableFrenchLocaleText(): any {
|
||||||
|
return {
|
||||||
|
// Messages d'erreur et états
|
||||||
|
noRowsToShow: 'Aucune ligne à afficher',
|
||||||
|
loadingOoo: 'Chargement...',
|
||||||
|
errorLoadingOoo: 'Erreur lors du chargement des données',
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
page: 'Page',
|
||||||
|
to: 'à',
|
||||||
|
of: 'sur',
|
||||||
|
nextPage: 'Page suivante',
|
||||||
|
lastPage: 'Dernière page',
|
||||||
|
firstPage: 'Première page',
|
||||||
|
previousPage: 'Page précédente',
|
||||||
|
pageSizeSelectorLabel: 'Lignes par page :',
|
||||||
|
|
||||||
|
// Filtres
|
||||||
|
equals: 'égal à',
|
||||||
|
notEqual: 'différent de',
|
||||||
|
lessThan: 'inférieur à',
|
||||||
|
lessThanOrEqual: 'inférieur ou égal à',
|
||||||
|
greaterThan: 'supérieur à',
|
||||||
|
greaterThanOrEqual: 'supérieur ou égal à',
|
||||||
|
inRange: 'dans la plage',
|
||||||
|
contains: 'contient',
|
||||||
|
notContains: 'ne contient pas',
|
||||||
|
startsWith: 'commence par',
|
||||||
|
endsWith: 'se termine par',
|
||||||
|
filterOoo: 'Filtrer...',
|
||||||
|
applyFilter: 'Appliquer',
|
||||||
|
clearFilter: 'Effacer',
|
||||||
|
andCondition: 'ET',
|
||||||
|
orCondition: 'OU',
|
||||||
|
|
||||||
|
// Menu de colonnes
|
||||||
|
pinColumn: 'Épingler la colonne',
|
||||||
|
pinLeft: 'Épingler à gauche',
|
||||||
|
pinRight: 'Épingler à droite',
|
||||||
|
noPin: 'Ne pas épingler',
|
||||||
|
valueColumn: 'Colonne de valeur',
|
||||||
|
groupBy: 'Grouper par',
|
||||||
|
ungroupBy: 'Dégrouper',
|
||||||
|
resetColumns: 'Réinitialiser les colonnes',
|
||||||
|
expandAll: 'Tout développer',
|
||||||
|
collapseAll: 'Tout réduire',
|
||||||
|
toolPanelButton: 'Panneau d’outils',
|
||||||
|
export: 'Exporter',
|
||||||
|
csvExport: 'Exporter en CSV',
|
||||||
|
excelExport: 'Exporter en Excel',
|
||||||
|
|
||||||
|
// Tri
|
||||||
|
sortAscending: 'Trier par ordre croissant',
|
||||||
|
sortDescending: 'Trier par ordre décroissant',
|
||||||
|
sortUnsort: 'Annuler le tri',
|
||||||
|
|
||||||
|
// Édition
|
||||||
|
enterValue: 'Entrer une valeur',
|
||||||
|
copy: 'Copier',
|
||||||
|
copyWithHeaders: 'Copier avec en-têtes',
|
||||||
|
paste: 'Coller',
|
||||||
|
cut: 'Couper',
|
||||||
|
|
||||||
|
// Groupes de lignes
|
||||||
|
group: 'Groupe',
|
||||||
|
ungroup: 'Dégrouper',
|
||||||
|
|
||||||
|
// Autres libellés courants
|
||||||
|
selectAll: 'Tout sélectionner',
|
||||||
|
searchOoo: 'Rechercher...',
|
||||||
|
blanks: '(Vide)',
|
||||||
|
notBlank: '(Non vide)',
|
||||||
|
rowGroupColumnsEmptyMessage: 'Glissez ici pour grouper',
|
||||||
|
pivotColumnsEmptyMessage: 'Glissez ici pour pivoter',
|
||||||
|
pivotMode: 'Mode pivot',
|
||||||
|
pivotOff: 'Désactiver pivot',
|
||||||
|
pivotOn: 'Activer pivot',
|
||||||
|
pivotChartTitle: 'Graphique pivot',
|
||||||
|
chartRangeTitle: 'Plage du graphique',
|
||||||
|
|
||||||
|
// Messages d'erreur avancés (optionnels)
|
||||||
|
aggregationWithoutAggregationFunction: 'Agrégation sans fonction d’agrégation',
|
||||||
|
pivotColumnNotSet: 'Colonne pivot non définie',
|
||||||
|
// Ajoute-en selon tes besoins
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
src/app/excel-export.service.ts
Normal file
48
src/app/excel-export.service.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
import * as FileSaver from 'file-saver';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ExcelExportService {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
private s2ab(s: string): ArrayBuffer {
|
||||||
|
const buf = new ArrayBuffer(s.length);
|
||||||
|
const view = new Uint8Array(buf);
|
||||||
|
for (let i = 0; i !== s.length; ++i) {
|
||||||
|
view[i] = s.charCodeAt(i) & 0xFF;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports JSON data to an XLSX file.
|
||||||
|
* @param data The array of JSON objects to export.
|
||||||
|
* @param fileName The desired name for the Excel file (without extension).
|
||||||
|
* @param sheetName The name of the sheet within the Excel file (defaults to 'Sheet1').
|
||||||
|
*/
|
||||||
|
exportAsExcelFile(data: any[], fileName: string, sheetName: string = 'Sheet1'): void {
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
console.warn('No data to export.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(data);
|
||||||
|
const wb: XLSX.WorkBook = { Sheets: { [sheetName]: ws }, SheetNames: [sheetName] };
|
||||||
|
const excelBuffer: any = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||||
|
this.saveAsExcelFile(excelBuffer, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveAsExcelFile(buffer: any, fileName: string): void {
|
||||||
|
const data: Blob = new Blob([buffer], { type: 'application/octet-stream' });
|
||||||
|
FileSaver.saveAs(data, fileName + '_export_' + new Date().getTime() + '.xlsx');
|
||||||
|
}
|
||||||
|
|
||||||
|
exportDataEnqueteParcelle(): any[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/app/filter-pipe.ts
Normal file
14
src/app/filter-pipe.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({name: 'filter'})
|
||||||
|
export class FilterPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: any, searchText: any): any {
|
||||||
|
if (!searchText) { return value; }
|
||||||
|
return value.filter((data: any) => this.matchValue(data, searchText));
|
||||||
|
}
|
||||||
|
|
||||||
|
matchValue(data: any, value: any) {
|
||||||
|
return Object.values(data).findIndex((element) => element && element?.toString()?.indexOf(value) > -1) > -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/app/global.resolver.ts
Normal file
20
src/app/global.resolver.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { CrudService } from './crud.service';
|
||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Resolve, ResolveFn, RouterStateSnapshot } from '@angular/router';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GlobalResolver implements Resolve<any> {
|
||||||
|
|
||||||
|
constructor(private service: CrudService) { }
|
||||||
|
|
||||||
|
resolve(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot
|
||||||
|
): Observable<any> | any {
|
||||||
|
let myParam = route.data['resolvedata'];
|
||||||
|
return this.service.getAll(myParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
202
src/app/global.service.ts
Normal file
202
src/app/global.service.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class GlobalService {
|
||||||
|
|
||||||
|
private _loding$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
private _paramData$ = new BehaviorSubject<any[]>([]);
|
||||||
|
|
||||||
|
private _bloc$ = new BehaviorSubject<any>(null);
|
||||||
|
|
||||||
|
private _paramURL$ = new BehaviorSubject<string>('/office');
|
||||||
|
|
||||||
|
private _enquete$ = new BehaviorSubject<any>(null);
|
||||||
|
|
||||||
|
private _commentaireEnquete$ = new BehaviorSubject<any>(null);
|
||||||
|
|
||||||
|
private _infoMap$ = new BehaviorSubject<any>(null);
|
||||||
|
|
||||||
|
private _enqueteCheckData$ = new BehaviorSubject<any[]>([]);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLodingSuccess(): Observable<boolean> {
|
||||||
|
return this._loding$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLodingSuccess(data: boolean): void {
|
||||||
|
this._loding$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParamData(): Observable<any[]> {
|
||||||
|
return this._paramData$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setParamData(data: any[]): void {
|
||||||
|
console.log('data next ==> ', data.length);
|
||||||
|
this._paramData$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBloc(): Observable<any> {
|
||||||
|
return this._bloc$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setBloc(data: any): void {
|
||||||
|
this._bloc$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEnquete(): Observable<any> {
|
||||||
|
return this._enquete$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setEnquete(data: any): void {
|
||||||
|
this._enquete$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCommentaireEnquete(): Observable<any> {
|
||||||
|
return this._commentaireEnquete$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setCommentaireEnquete(data: any): void {
|
||||||
|
this._commentaireEnquete$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParamURL(): Observable<string> {
|
||||||
|
return this._paramURL$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setParamURL(data: string): void {
|
||||||
|
this._paramURL$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getInfoMap(): Observable<any> {
|
||||||
|
return this._infoMap$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setInfoMap(data: any): void {
|
||||||
|
this._infoMap$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public getEnqueteCheckData(): Observable<any[]> {
|
||||||
|
return this._enqueteCheckData$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setEnqueteCheckData(data: any[]): void {
|
||||||
|
this._enqueteCheckData$.next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getModuleList(): any[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Tableau de bord',
|
||||||
|
description: 'Suivi global des enquêtes foncières fiscales',
|
||||||
|
detail: "Tableau de bord pour suivre l'avancement des enquêtes fiscales.",
|
||||||
|
icone: 'team.svg',
|
||||||
|
classIcon: 'div-icon-dash-dark-header',
|
||||||
|
classIconBody: 'div-icon-dash-dark',
|
||||||
|
link: '/dashboard',
|
||||||
|
color: '#2c2c2c',
|
||||||
|
dash: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module référence',
|
||||||
|
description: "Gestion des données de référence",
|
||||||
|
detail: "Traitement des données de référence : type parcelle etc.",
|
||||||
|
icone: 'recouvrement.svg',
|
||||||
|
classIcon: 'div-icon-dash-dark-header',
|
||||||
|
classIconBody: 'div-icon-dash-dark',
|
||||||
|
link: '/core/reference/sommaire-reference',
|
||||||
|
color: 'rgb(20 97 59)',
|
||||||
|
dash: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module liquidation',
|
||||||
|
description: "Gestion des données de liquidation",
|
||||||
|
detail: "Traitement des données de liquidation : taxe TFU etc.",
|
||||||
|
icone: 'solutions-white.svg',
|
||||||
|
classIcon: 'div-icon-dash-dark-header',
|
||||||
|
classIconBody: 'div-icon-dash-dark',
|
||||||
|
link: '/core/liquidation/sommaire-liquidation',
|
||||||
|
color: 'rgb(145, 66, 66)',
|
||||||
|
dash: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module Consultation',
|
||||||
|
description: 'Dossier en cours sur le module consultation',
|
||||||
|
detail: 'Vous pouvez accéder au module consultation : consultation des parcelles, des bâtiments etc.',
|
||||||
|
icone: 'gear-user.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/consultation/sommaire-consultation',
|
||||||
|
color: 'rgb(35, 114, 121)',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module Cartographie',
|
||||||
|
description: 'Dossier en cours sur le module cartographie',
|
||||||
|
detail: 'Vous pouvez accéder au module cartographie : visualisation cartographique des immeubles etc.',
|
||||||
|
icone: 'tiers.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/cartographie/data/sommaire-cartographie',
|
||||||
|
color: '#1a5890',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module Enregistrement',
|
||||||
|
description: 'Dossier en cours sur le module enregistrement',
|
||||||
|
detail: 'Vous pouvez accéder au module enregistrement : création des parcelles, des bâtiments',
|
||||||
|
icone: 'solutions-white.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/enregistrement/sommaire-enregistrement',
|
||||||
|
color: 'rgb(20 97 59)',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'Module Recherche avancée',
|
||||||
|
description: 'Dossier en cours sur le module recherche avancée',
|
||||||
|
detail: 'Vous pouvez accéder au module recherche avancée : recherche de parcelles, de bâtiments etc.',
|
||||||
|
icone: 'search-white.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/recherche-avancee/avis-contribuable',
|
||||||
|
color: 'rgb(205, 145, 28)',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module Administration',
|
||||||
|
description: 'Dossier en cours sur le module utilisateur',
|
||||||
|
detail: 'Vous pouvez accéder au traitement des dossiers du module Administration : nouvel utilisateur, désactivation, etc.',
|
||||||
|
icone: 'team.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/utilisateur/sommaire',
|
||||||
|
color: 'rgb(97 92 20)',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Module Organisation',
|
||||||
|
description: 'Dossier en cours sur le module organisation',
|
||||||
|
detail: 'Vous pouvez accéder au traitement des dossiers du module Organisation : création des secteurs, des équipes etc.',
|
||||||
|
icone: 'calculator.svg',
|
||||||
|
classIcon: 'div-icon-dash-header',
|
||||||
|
classIconBody: 'div-icon-dash',
|
||||||
|
link: '/core/programmation/sommaire-organisation',
|
||||||
|
color: 'rgb(32, 56, 100)',
|
||||||
|
dash: false
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
23
src/app/home/home-routing.module.ts
Normal file
23
src/app/home/home-routing.module.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
import { LoginComponent } from './login/login.component';
|
||||||
|
import { SingupComponent } from './singup/singup.component';
|
||||||
|
import { RequestPasswordComponent } from './request-password/request-password.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{path: '', component: HomeComponent,
|
||||||
|
children: [
|
||||||
|
{ path: '', component: LoginComponent },
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'singup', component: SingupComponent },
|
||||||
|
{ path: 'request-password', component: RequestPasswordComponent },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class HomeRoutingModule { }
|
||||||
56
src/app/home/home.component.css
Normal file
56
src/app/home/home.component.css
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
.parent {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Centrage horizontal */
|
||||||
|
/*align-items: center; Centrage vertical */
|
||||||
|
height: 100%; /* Ou une hauteur fixe, ex: 400px */
|
||||||
|
margin-left: 30%;
|
||||||
|
flex-direction: column; /* ← Les enfants s'empilent verticalement */
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-login-logo {
|
||||||
|
margin-left: 30%;
|
||||||
|
margin-top: 5% !important;
|
||||||
|
color: #c1ffc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 8px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin-top: -2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag>li:first-child {
|
||||||
|
background: RGB(16, 135, 87);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag>li {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 33.33%;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
vertical-align: middle;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag>li:first-child+li {
|
||||||
|
background: RGB(255, 190, 0);
|
||||||
|
width: 33.34%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag li:first-child+li+li {
|
||||||
|
background: RGB(235, 0, 0);
|
||||||
|
}
|
||||||
54
src/app/home/home.component.html
Normal file
54
src/app/home/home.component.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="row" style="background: #0f3625;background-image: url(/assets/fond-login.2790fbe0.webp);background-repeat: no-repeat;background-position: left center;background-size: cover;">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="title-login-logo">
|
||||||
|
<div class="display-block">
|
||||||
|
<!--<h3>FISCAD</h3>-->
|
||||||
|
<img src="assets/logo-2.8886fece.png" alt="" style="width: 57%;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="parent" style="margin-top: -18%;">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="display-block" style="font-weight: bold;">
|
||||||
|
<p style="font-size: 2.7em;color:white;line-height: 1.5em;"> Bienvenue sur </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="display-block">
|
||||||
|
<h2 style="color: #fc0;display: inline;font-size: 8.3em;font-weight: 600;"> SIGIBé </h2> <br>
|
||||||
|
<span style="color: white;font-style: italic;font-size: 4em;line-height: 10px;">Foncier <span style="height: 1px;color: #fc0;"> </span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="display-block" style="margin-top: 15px;">
|
||||||
|
<h6 class="display-block" style="color:hsla(0,0%,100%,.478);letter-spacing: .3px;opacity: .8;font-style: italic;"> Système de Gestion Unifiée des Impôts Fonciers </h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 text-center">
|
||||||
|
<img src="assets/static/fond_carte_login.png" alt="" style="width: 110px;margin-top: -35%;">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
|
||||||
|
<div class="flag-container" style="margin-top: -0.5%;">
|
||||||
|
<ul class="flag">
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
10
src/app/home/home.component.ts
Normal file
10
src/app/home/home.component.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent {
|
||||||
|
|
||||||
|
}
|
||||||
30
src/app/home/home.module.ts
Normal file
30
src/app/home/home.module.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { HomeRoutingModule } from './home-routing.module';
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
import { LoginComponent } from './login/login.component';
|
||||||
|
import { SingupComponent } from './singup/singup.component';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
import { RequestPasswordComponent } from './request-password/request-password.component';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
HomeComponent,
|
||||||
|
LoginComponent,
|
||||||
|
SingupComponent,
|
||||||
|
RequestPasswordComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
HomeRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
SharedModule,
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class HomeModule { }
|
||||||
14
src/app/home/login/login.component.css
Normal file
14
src/app/home/login/login.component.css
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
.parent {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Centrage horizontal */
|
||||||
|
/*align-items: center; Centrage vertical */
|
||||||
|
height: 100%; /* Ou une hauteur fixe, ex: 400px */
|
||||||
|
margin-left: 35%;
|
||||||
|
flex-direction: column; /* ← Les enfants s'empilent verticalement */
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-login-logo {
|
||||||
|
margin-left: 35%;
|
||||||
|
margin-top: 5% !important;
|
||||||
|
color: #c1ffc1;
|
||||||
|
}
|
||||||
119
src/app/home/login/login.component.html
Normal file
119
src/app/home/login/login.component.html
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
<div class="container-fluid page-body-wrapper full-page-wrapper auth-page">
|
||||||
|
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||||
|
<div class="row w-100">
|
||||||
|
<div class="col-lg-8 mx-auto">
|
||||||
|
<div class="auto-form-wrapper">
|
||||||
|
<div nz-row class="text-center">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<img src="assets/static/images/logo-impot.jfif" alt=""
|
||||||
|
style="width: 25%;margin-top: -30%;border-radius: 50%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="24" class="my-3 text-center">
|
||||||
|
<h3 nz-typography class="text-primary mb-4"> <strong> CONNEXION </strong></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="validateForm" *ngIf="validateForm && isPasswordForm == false" role="form"
|
||||||
|
class="php-email-form">
|
||||||
|
<nz-alert nzType="error" nzMessage="Erreur" nzCloseable class="mb-15"
|
||||||
|
[nzDescription]="contenu" nzShowIcon *ngIf="contenu != ''"
|
||||||
|
(nzOnClose)="createNotification('', '')">
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="email" id="username"
|
||||||
|
formControlName="username" placeholder="">
|
||||||
|
<label class="did-floating-label"> <span nz-icon nzType="user"></span> Entrer Votre
|
||||||
|
identifiant </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="did-floating-label-content" style="margin-top: 8%;">
|
||||||
|
<input class="did-floating-input" type="password" id="password"
|
||||||
|
formControlName="password" placeholder="">
|
||||||
|
<label class="did-floating-label"> <span nz-icon nzType="lock"></span> Entrer Votre
|
||||||
|
mot de
|
||||||
|
passe </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<button nz-button nzType="primary" nzBlock class="shadow" class="min-heigth-45"
|
||||||
|
(click)="submit()" [disabled]="isActionInProgress">
|
||||||
|
{{ isActionInProgress == false ? 'ME CONNECTER' : 'CONNEXION EN COURS ...' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-divider nzText="OU"></nz-divider>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="12" class="text-center"
|
||||||
|
style="border-right: solid 2px #801C06;">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/singup')" style="font-size: 12px;">
|
||||||
|
S'inscrire
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="text-center">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/request-password')" style="font-size: 12px;">
|
||||||
|
Mot de passe oublié ?
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<form [formGroup]="passwordForm" *ngIf="passwordForm && isPasswordForm == true" role="form"
|
||||||
|
class="php-email-form">
|
||||||
|
<nz-alert nzType="success" nzMessage="Information" class="mb-15"
|
||||||
|
[nzDescription]="passwordChangeMessage" nzShowIcon>
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<nz-alert nzType="error" nzMessage="Erreur" nzCloseable class="mb-15"
|
||||||
|
[nzDescription]="contenu" nzShowIcon *ngIf="contenu != ''"
|
||||||
|
(nzOnClose)="createNotification('', '')">
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<div class="did-floating-label-content" style="margin-top: 8%;">
|
||||||
|
<input class="did-floating-input" type="password" id="password"
|
||||||
|
formControlName="password" placeholder="" (blur)="confirmationPassword()">
|
||||||
|
<label class="did-floating-label"> <span nz-icon nzType="lock"></span> Nouveau mot
|
||||||
|
de passe </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="password" id="confirmation"
|
||||||
|
formControlName="confirmation" placeholder="" (blur)="confirmationPassword()">
|
||||||
|
<label class="did-floating-label"> <span nz-icon nzType="user"></span> Confirmation
|
||||||
|
du mot de passe </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<button nz-button nzType="primary" nzBlock class="shadow" class="min-heigth-45"
|
||||||
|
(click)="changerPassword()" [disabled]="isActionInProgress">
|
||||||
|
{{ isActionInProgress == false ? 'VALIDER' : 'VALIDATION EN COURS ...' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<p class="footer-text text-center my-3" style="color:hsla(0,0%,100%,.478);font-size: 11px;"> <strong> Copyright © 2026 ABS Technology. Tous
|
||||||
|
droits réservés.
|
||||||
|
</strong></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
</div>
|
||||||
|
|
||||||
177
src/app/home/login/login.component.ts
Normal file
177
src/app/home/login/login.component.ts
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrls: ['./login.component.css']
|
||||||
|
})
|
||||||
|
export class LoginComponent implements OnInit {
|
||||||
|
|
||||||
|
validateForm!: FormGroup;
|
||||||
|
|
||||||
|
passwordForm!: FormGroup;
|
||||||
|
|
||||||
|
passwordShow = false;
|
||||||
|
|
||||||
|
redirectURL = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
isPasswordForm = false;
|
||||||
|
passwordChangeMessage = '';
|
||||||
|
|
||||||
|
type = 'info';
|
||||||
|
|
||||||
|
contenu: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.validateForm = this.fb.group({
|
||||||
|
username: [null, [Validators.required]],
|
||||||
|
password: [null, [Validators.required]],
|
||||||
|
remember: [false]
|
||||||
|
});
|
||||||
|
this.passwordForm = this.fb.group({
|
||||||
|
confirmation: [null, [Validators.required]],
|
||||||
|
password: [null, [Validators.required]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification(type: string, content: string): void {
|
||||||
|
this.type = type;
|
||||||
|
this.contenu = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto(url: string): void {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
for (const i in this.validateForm?.controls) {
|
||||||
|
this.validateForm?.controls[i].markAsDirty();
|
||||||
|
this.validateForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.validateForm?.valid) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
this.isPasswordForm = false;
|
||||||
|
const formData = this.validateForm?.value;
|
||||||
|
delete formData.remember;
|
||||||
|
this.crudService.save('auth/login', formData).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
if (data.success == true) {
|
||||||
|
/* if (this.isRoles(['ROLE_ENQUETEUR'], data.object?.token) == true) {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur !',
|
||||||
|
nzContent: "Votre profil n'est pas éligible pour vous connecter."
|
||||||
|
});
|
||||||
|
} else {*/
|
||||||
|
this.tokenStorage.saveToken(data.object?.token);
|
||||||
|
this.router.navigate(['/principale']);
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
if (data.object == null) {
|
||||||
|
this.createNotification('error', data.message);
|
||||||
|
} else {
|
||||||
|
/* if (this.isRoles(['ROLE_ENQUETEUR'], data.object?.token) == true) {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur !',
|
||||||
|
nzContent: "Votre profil n'est pas éligible pour vous connecter."
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.tokenStorage.saveToken(data.object?.token);
|
||||||
|
this.passwordChangeMessage = data.message;
|
||||||
|
this.isPasswordForm = true;
|
||||||
|
this.validateForm.reset();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.createNotification('error', 'Veuillez renseigner obligatoirement vos identifiants.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changerPassword(): void {
|
||||||
|
for (const i in this.passwordForm?.controls) {
|
||||||
|
this.passwordForm?.controls[i].markAsDirty();
|
||||||
|
this.passwordForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.passwordForm?.valid) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodedToken = helper.decodeToken(token ? token : '');
|
||||||
|
console.log(decodedToken);
|
||||||
|
const formData = this.passwordForm?.value;
|
||||||
|
this.crudService.save('user/change-password', { "password": formData.password, "username": decodedToken?.sub }).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
if (data.success == true) {
|
||||||
|
this.tokenStorage.signOut();
|
||||||
|
this.message.create('error', data.message);
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
this.modal.success({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.createNotification('error', 'Veuillez renseigner obligatoirement les champs.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
confirmationPassword(): void {
|
||||||
|
const formData = this.passwordForm?.value;
|
||||||
|
if (formData.password != null && formData.confirmation != null
|
||||||
|
&& formData.password.trim() != '' && formData.confirmation.trim() != '' &&
|
||||||
|
formData.confirmation.trim() != formData.password) {
|
||||||
|
this.passwordForm?.get('confirmation')?.setValue(null);
|
||||||
|
this.message.create('error', `Erreur dans la confirmation du mot de passe.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isRoles(params: any[], token: string): boolean {
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodedToken = helper.decodeToken(token);
|
||||||
|
console.log(decodedToken);
|
||||||
|
if (decodedToken && decodedToken.user != null && decodedToken.user.avoirFonctions.length > 0) {
|
||||||
|
return params.indexOf(decodedToken.user.avoirFonctions[0]?.nom) > -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<div class="container-fluid page-body-wrapper full-page-wrapper auth-page">
|
||||||
|
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||||
|
<div class="row w-100">
|
||||||
|
<div class="col-lg-8 mx-auto">
|
||||||
|
<div class="auto-form-wrapper">
|
||||||
|
<div nz-row class="text-center">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<img src="assets/static/images/logo-impot.jfif" alt=""
|
||||||
|
style="width: 25%;margin-top: -30%;border-radius: 50%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="24" class="my-3 text-center">
|
||||||
|
<h3 nz-typography class="text-primary mb-4"> <strong> DEMANDE DE RÉINITIALISATION </strong>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="24" class="bg-white">
|
||||||
|
|
||||||
|
<form [formGroup]="validateForm" *ngIf="validateForm" role="form">
|
||||||
|
<nz-alert nzType="error" nzMessage="Erreur" nzCloseable class="mb-10"
|
||||||
|
[nzDescription]="contenu" nzShowIcon *ngIf="contenu != ''"
|
||||||
|
(nzOnClose)="createNotification('', '')">
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<nz-alert nzType="warning" class="mb-10"
|
||||||
|
[nzDescription]="'Soumettez le formulaire suivant pour effectuer votre demande de réinitialisation de mot de passe'">
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="email" id="email" formControlName="email"
|
||||||
|
placeholder="">
|
||||||
|
<label class="did-floating-label"> <span nz-icon nzType="mail"></span> Entrer
|
||||||
|
Votre adresse email </label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<button nz-button nzType="primary" nzBlock class="shadow" class="min-heigth-45"
|
||||||
|
(click)="changerPassword()" [disabled]="isActionInProgress">
|
||||||
|
{{ isActionInProgress == false ? 'RÉINITIALISER' : 'DEMANDE EN COURS ...' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-divider nzText="OU"></nz-divider>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="12" class="text-center" style="border-right: solid 2px #801C06;">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/login')" style="font-size: 12px;">
|
||||||
|
Se connecter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="text-center">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/singup')" style="font-size: 12px;">
|
||||||
|
S'inscrire
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<p class="footer-text text-center my-3" style="color:hsla(0,0%,100%,.478);font-size: 11px;"> <strong>
|
||||||
|
Copyright © 2026 ABS Technology. Tous
|
||||||
|
droits réservés.
|
||||||
|
</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
</div>
|
||||||
100
src/app/home/request-password/request-password.component.ts
Normal file
100
src/app/home/request-password/request-password.component.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-request-password',
|
||||||
|
templateUrl: './request-password.component.html',
|
||||||
|
styleUrls: ['./request-password.component.css']
|
||||||
|
})
|
||||||
|
export class RequestPasswordComponent implements OnInit {
|
||||||
|
|
||||||
|
validateForm!: FormGroup;
|
||||||
|
|
||||||
|
passwordShow = false;
|
||||||
|
actionProgress = false;
|
||||||
|
|
||||||
|
redirectURL = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
|
||||||
|
type = 'info';
|
||||||
|
contenu: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
this.validateForm = this.fb.group({
|
||||||
|
email: [null, [Validators.required]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification(type: string, content: string): void {
|
||||||
|
this.type = type;
|
||||||
|
this.contenu = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto(url: string): void {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
changerPassword(): void {
|
||||||
|
for (const i in this.validateForm?.controls) {
|
||||||
|
this.validateForm?.controls[i].markAsDirty();
|
||||||
|
this.validateForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
const formData = this.validateForm?.value;
|
||||||
|
if (this.validateForm?.valid) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
this.crudService.getAll('demande-reinitialisation-mp/create?usernamrOrEmail=' + formData.email).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
if (data.success == true) {
|
||||||
|
this.tokenStorage.signOut();
|
||||||
|
this.message.create('success', 'Opération effectuée avec succès');
|
||||||
|
this.modal.success({
|
||||||
|
nzTitle: 'Information',
|
||||||
|
nzContent: data.message,
|
||||||
|
nzOnOk: ()=> {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
nzOnCancel: ()=> {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.message.create('error', data.message);
|
||||||
|
this.createNotification('', data.message)
|
||||||
|
}
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.createNotification('', 'Formulaire invalid.')
|
||||||
|
/*this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: 'Formulaire invalid.'
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
0
src/app/home/singup/singup.component.css
Normal file
0
src/app/home/singup/singup.component.css
Normal file
123
src/app/home/singup/singup.component.html
Normal file
123
src/app/home/singup/singup.component.html
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
<div class="container-fluid page-body-wrapper full-page-wrapper auth-page">
|
||||||
|
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
|
||||||
|
<div class="row w-100">
|
||||||
|
<div class="col-lg-10 mx-auto">
|
||||||
|
<div class="auto-form-wrapper">
|
||||||
|
<div nz-row class="text-center">
|
||||||
|
<div nz-col nzSpan="24">
|
||||||
|
<img src="assets/static/images/logo-impot.jfif" alt=""
|
||||||
|
style="width: 20%;margin-top: -20%;border-radius: 50%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="24" class="my-3 text-center">
|
||||||
|
<h3 nz-typography class="text-primary mb-4"> <strong> INSCRIPTION </strong></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="validateForm" *ngIf="validateForm" role="form" class="php-email-form">
|
||||||
|
<nz-alert nzType="error" nzMessage="Erreur" nzCloseable class="mb-15" [nzDescription]="contenu"
|
||||||
|
nzShowIcon *ngIf="contenu != ''" (nzOnClose)="createNotification('', '')">
|
||||||
|
</nz-alert>
|
||||||
|
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="text" id="nom"
|
||||||
|
formControlName="nom" placeholder="">
|
||||||
|
<label class="did-floating-label"> Nom <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="text" id="prenom"
|
||||||
|
formControlName="prenom" placeholder="">
|
||||||
|
<label class="did-floating-label"> Prénom(s) <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="text" id="telephone"
|
||||||
|
formControlName="telephone" placeholder="">
|
||||||
|
<label class="did-floating-label"> Téléphone <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="email" id="email"
|
||||||
|
formControlName="email" placeholder="">
|
||||||
|
<label class="did-floating-label"> Email <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div nz-row>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="password" id="password"
|
||||||
|
formControlName="password" placeholder="" (blur)="confirmationPassword()">
|
||||||
|
<label class="did-floating-label"> Mot de passe <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="p-1">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="password" id="confirmation"
|
||||||
|
formControlName="confirmation" placeholder="" (blur)="confirmationPassword()">
|
||||||
|
<label class="did-floating-label"> Confirmation <span class="text-danger"> * </span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner le centre de gestion"
|
||||||
|
formControlName="structureId">
|
||||||
|
<nz-option *ngFor="let item of structureList" [nzLabel]="item.nom" [nzValue]="item.id"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -13px;"> Sélectionnez le centre de gestion <span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="24" class="p-1">
|
||||||
|
<button nz-button nzType="primary" [disabled]="isActionInProgress"
|
||||||
|
nzBlock class="shadow" class="min-heigth-45" (click)="submit()">
|
||||||
|
{{ isActionInProgress == false ? "M'INSCRIRE" : loadingMessage }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-divider nzText="OU"></nz-divider>
|
||||||
|
|
||||||
|
<div nz-row class="mb-20">
|
||||||
|
<div nz-col nzSpan="12" class="text-center" style="border-right: solid 2px #801C06;">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/login')" style="font-size: 12px;">
|
||||||
|
Se connecter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div nz-col nzSpan="12" class="text-center">
|
||||||
|
<button nz-button nzType="primary" nzType="link" class="min-heigth-45"
|
||||||
|
(click)="goto('/request-password')" style="font-size: 12px;">
|
||||||
|
Mot de passe oublié ?
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- content-wrapper ends -->
|
||||||
|
</div>
|
||||||
140
src/app/home/singup/singup.component.ts
Normal file
140
src/app/home/singup/singup.component.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-singup',
|
||||||
|
templateUrl: './singup.component.html',
|
||||||
|
styleUrls: ['./singup.component.css']
|
||||||
|
})
|
||||||
|
export class SingupComponent implements OnInit {
|
||||||
|
|
||||||
|
validateForm!: FormGroup;
|
||||||
|
|
||||||
|
passwordShow = false;
|
||||||
|
|
||||||
|
redirectURL = null;
|
||||||
|
structureList: any[] = [];
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
loadingMessage ='';
|
||||||
|
|
||||||
|
type = 'info';
|
||||||
|
|
||||||
|
contenu: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.validateForm = this.fb.group({
|
||||||
|
nom: [null, [Validators.required]],
|
||||||
|
prenom: [null, [Validators.required]],
|
||||||
|
telephone: [null, [Validators.required]],
|
||||||
|
email: [null, [Validators.required]],
|
||||||
|
password: [null, [Validators.required]],
|
||||||
|
confirmation: [null, [Validators.required]],
|
||||||
|
structureId: [null, [Validators.required]]
|
||||||
|
});
|
||||||
|
this.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification(type: string, content: string): void {
|
||||||
|
this.type = type;
|
||||||
|
this.contenu = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
compareFn = (o1: any, o2: any) => (o1 && o2 ? o1.id === o2.id : o1 === o2);
|
||||||
|
|
||||||
|
list(): void {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
this.loadingMessage = 'Chargement des données ...';
|
||||||
|
this.structureList = [];
|
||||||
|
this.crudService.getAll('structure/all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.structureList = data != null ? data.object : [];
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
this.loadingMessage = '';
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
this.loadingMessage = '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
goto(url: string): void {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmationPassword(): void {
|
||||||
|
const formData = this.validateForm?.value;
|
||||||
|
if (formData.password != null && formData.confirmation != null
|
||||||
|
&& formData.password.trim() != '' && formData.confirmation.trim() != '' &&
|
||||||
|
formData.confirmation.trim() != formData.password) {
|
||||||
|
this.validateForm?.get('confirmation')?.setValue(null);
|
||||||
|
this.message.create('error', `Erreur dans la confirmation du mot de passe.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
for (const i in this.validateForm?.controls) {
|
||||||
|
this.validateForm?.controls[i].markAsDirty();
|
||||||
|
this.validateForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.validateForm?.valid) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
this.loadingMessage = 'INSCRIPTION EN COURS ...';
|
||||||
|
const formData = this.validateForm?.value;
|
||||||
|
this.crudService.save('auth/signup', formData).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
if (data.success == true) {
|
||||||
|
this.modal.success({
|
||||||
|
nzTitle: 'Succès !',
|
||||||
|
nzContent: data.message,
|
||||||
|
nzOnOk: ()=> {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
},
|
||||||
|
nzOnCancel: ()=> {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.createNotification('error', data.message);
|
||||||
|
}
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
this.loadingMessage = '';
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
this.loadingMessage = '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.createNotification('error', 'Veuillez renseigner correctement tous les champs.');
|
||||||
|
/*this.modal.error({
|
||||||
|
nzTitle: 'Erreur !',
|
||||||
|
nzContent: 'Veuillez renseigner obligatoirement vos identifiants.'
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
21
src/app/manage/manage-routing.module.ts
Normal file
21
src/app/manage/manage-routing.module.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { ManageComponent } from './manage.component';
|
||||||
|
import { MonCompteComponent } from '../shared/mon-compte/mon-compte.component';
|
||||||
|
import { ResetPasswordComponent } from '../shared/reset-password/reset-password.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '', component: ManageComponent,
|
||||||
|
children: [
|
||||||
|
{ path: 'mon-compte', component: MonCompteComponent },
|
||||||
|
{ path: 'reset-password', component: ResetPasswordComponent },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class ManageRoutingModule { }
|
||||||
9
src/app/manage/manage.component.css
Normal file
9
src/app/manage/manage.component.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.badge-success {
|
||||||
|
background: #04cd6761;
|
||||||
|
color: #14613b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-danger {
|
||||||
|
background: #ff001829;
|
||||||
|
color: #fe0118;
|
||||||
|
}
|
||||||
367
src/app/manage/manage.component.html
Normal file
367
src/app/manage/manage.component.html
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<nav class="navbar default-layout col-lg-12 col-12 p-0 fixed-top d-flex flex-row"
|
||||||
|
style="background: #ffff;height: 80px!important;box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 30px 0px;">
|
||||||
|
<div class="text-center navbar-brand-wrapper d-flex align-items-top justify-content-center"
|
||||||
|
style="align-items: center;">
|
||||||
|
<a class="navbar-brand brand-logo" href="">
|
||||||
|
<img src="assets/logo2.png" alt="logo" style="height: 50px!important;" />
|
||||||
|
</a>
|
||||||
|
<a class="navbar-brand brand-logo-mini" href="">
|
||||||
|
<img src="assets/logo2.png" alt="logo" style="height: 50px !important;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#">
|
||||||
|
<img src="assets/sigibe/menu-black.svg" alt="logo" style="height: 16px;margin-left: 10px;" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu-wrapper d-flex align-items-center">
|
||||||
|
|
||||||
|
<button class="btn btn-primary btn-fw btn-select-module" style="height: 40px;background: #2c2c2c;border: none;"
|
||||||
|
nz-popover nzPopoverTitle="Modules applicatifs" [(nzPopoverVisible)]="visible"
|
||||||
|
(nzPopoverVisibleChange)="change($event)" nzPopoverTrigger="click" [nzPopoverContent]="contentTemplate"
|
||||||
|
nzPopoverPlacement="rightBottom">
|
||||||
|
<i class="mdi mdi-settings" style="font-size: 15px;"> </i>
|
||||||
|
<span> Sélectionner un module </span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ng-template #contentTemplate>
|
||||||
|
|
||||||
|
<nz-list>
|
||||||
|
|
||||||
|
<nz-list-item *ngFor="let item of moduleList" class="review-list-item"
|
||||||
|
(click)="selectModuleAccess(item)">
|
||||||
|
<div class="p-2 {{item.classIcon}}"
|
||||||
|
[ngStyle]="{ backgroundColor: item && item.color ? item.color : '' }">
|
||||||
|
<img src="assets/sigibe/{{ item.icone }}" alt="">
|
||||||
|
</div>
|
||||||
|
<div style="line-height: 5px;">
|
||||||
|
<h3 style="font-size: 12px;margin-left: 10px;color: #3cb22a;"> {{ item.title }} </h3>
|
||||||
|
<h6 style="font-size: 12px;margin-left: 10px;color: rgb(99, 99, 99);font-weight: 900;">
|
||||||
|
{{ item.description }}
|
||||||
|
</h6>
|
||||||
|
<!--<p style="font-size: 11px;margin-left: 10px;color: rgb(99, 99, 99);line-height: 16px;margin-bottom: 0px;">
|
||||||
|
{{ item.detail }}
|
||||||
|
</p>-->
|
||||||
|
</div>
|
||||||
|
</nz-list-item>
|
||||||
|
|
||||||
|
</nz-list>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ul class="navbar-nav navbar-nav-right">
|
||||||
|
<!--<li class="nav-item">
|
||||||
|
<a class="nav-link count-indicator ico-header"
|
||||||
|
(click)="goto('/administration/enquete/lot-validation-rejet')">
|
||||||
|
<i class="mdi mdi-check"></i>
|
||||||
|
<span class="count icon-notify" *ngIf="enqueteCheckList.length > 0">
|
||||||
|
{{ enqueteCheckList.length }}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>-->
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link count-indicator dropdown-toggle ico-header" id="notificationDropdown"
|
||||||
|
data-toggle="dropdown" style="margin-right: 30px;">
|
||||||
|
<i class="mdi mdi-bell"></i>
|
||||||
|
<span class="count icon-notify" style="width: auto !important; min-width: 18px; padding-right: 5px; padding-left: 5px;" *ngIf="getTotalNotification() > 0">
|
||||||
|
<span> {{ getTotalNotification() }} </span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right navbar-dropdown preview-list"
|
||||||
|
aria-labelledby="notificationDropdown" *ngIf="getTotalNotification() > 0">
|
||||||
|
<a class="dropdown-item">
|
||||||
|
<p class="mb-0 font-weight-normal float-left">
|
||||||
|
Vous avez <span style="margin-left: 0;font-weight: bold;"
|
||||||
|
[ngClass]="getTotalNotification() > 0 ? 'badge badge-danger': 'badge badge-success'">
|
||||||
|
{{ getTotalNotification() }} </span> notifications en attentes
|
||||||
|
</p>
|
||||||
|
<span class="badge badge-pill badge-warning float-right"
|
||||||
|
(click)="goto('/core/enregistrement/fiche-validation-enquete-parcelle')" style="cursor: pointer;">
|
||||||
|
Voir tout
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
|
<a class="dropdown-item preview-item" (click)="goto('/core/enregistrement/fiche-validation-enquete-parcelle')">
|
||||||
|
<div class="preview-thumbnail">
|
||||||
|
<div class="preview-icon bg-success">
|
||||||
|
<i class="mdi mdi-map-marker-outline mx-0"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview-item-content">
|
||||||
|
<h6 class="preview-subject font-weight-medium text-dark">
|
||||||
|
Parcelles
|
||||||
|
</h6>
|
||||||
|
<p class="font-weight-light small-text">
|
||||||
|
<span style="margin-left: 0;font-weight: bold;"
|
||||||
|
[ngClass]="notifEnqueteList?.nombreEnqueteParcelle > 0 ? 'badge badge-danger': 'badge badge-success'">
|
||||||
|
{{ notifEnqueteList?.nombreEnqueteParcelle ?? 0 }} </span>
|
||||||
|
enquêtes parcelles en attente
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item preview-item" (click)="goto('/core/enregistrement/fiche-validation-enquete-batiment')">
|
||||||
|
<div class="preview-thumbnail">
|
||||||
|
<div class="preview-icon bg-success">
|
||||||
|
<i class="mdi mdi-home mx-0"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview-item-content">
|
||||||
|
<h6 class="preview-subject font-weight-medium text-dark">
|
||||||
|
Bâtiments
|
||||||
|
</h6>
|
||||||
|
<p class="font-weight-light small-text">
|
||||||
|
<span style="margin-left: 0;font-weight: bold;"
|
||||||
|
[ngClass]="notifEnqueteList?.nombreEnqueteBatiment > 0 ? 'badge badge-danger': 'badge badge-success'">
|
||||||
|
{{ notifEnqueteList?.nombreEnqueteBatiment ?? 0 }} </span>
|
||||||
|
enquêtes bâtiments en attente
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item preview-item" (click)="goto('/core/enregistrement/fiche-validation-enquete-unite-logement')">
|
||||||
|
<div class="preview-thumbnail">
|
||||||
|
<div class="preview-icon bg-success">
|
||||||
|
<i class="mdi mdi-grid mx-0"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="preview-item-content">
|
||||||
|
<h6 class="preview-subject font-weight-medium text-dark">
|
||||||
|
Unités de logement
|
||||||
|
</h6>
|
||||||
|
<p class="font-weight-light small-text">
|
||||||
|
<span style="margin-left: 0;font-weight: bold;"
|
||||||
|
[ngClass]="notifEnqueteList?.nombreEnqueteUniteLogement > 0 ? 'badge badge-danger': 'badge badge-success'">
|
||||||
|
{{ notifEnqueteList?.nombreEnqueteUniteLogement ?? 0 }} </span>
|
||||||
|
enquêtes unités de logement en attente
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown d-none d-xl-inline-block">
|
||||||
|
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown"
|
||||||
|
aria-expanded="false">
|
||||||
|
<span class="profile-text"
|
||||||
|
style="color: #333;">{{ user ? (user.nom | uppercase) : 'Inconnu' }}</span>
|
||||||
|
<img class="img-xs rounded-circle" src="assets/static/avatar.png" alt="Profile image">
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
||||||
|
<a class="dropdown-item p-0">
|
||||||
|
<div class="d-flex border-bottom">
|
||||||
|
<div class="py-3 px-4 d-flex align-items-center justify-content-center"
|
||||||
|
(click)="goto('/principale/reset-password')">
|
||||||
|
<i class="mdi mdi-settings mr-0 text-gray"></i>
|
||||||
|
</div>
|
||||||
|
<div (click)="goto('/principale/mon-compte')"
|
||||||
|
class="py-3 px-4 d-flex align-items-center justify-content-center border-left border-right">
|
||||||
|
<i class="mdi mdi-account-outline mr-0 text-gray"></i>
|
||||||
|
</div>
|
||||||
|
<div class="py-3 px-4 d-flex align-items-center justify-content-center"
|
||||||
|
(click)="goto('/principale')">
|
||||||
|
<i class="mdi mdi-chart-line mr-0 text-gray"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item mt-2" routerLinkActive="active-menu-link-user"
|
||||||
|
[routerLink]="['/core/mon-compte']">
|
||||||
|
<i class="mdi mdi-account-outline mr-2 text-gray"></i> Mon compte
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" routerLinkActive="active-menu-link-user"
|
||||||
|
[routerLink]="['/core/reset-password']">
|
||||||
|
<i class="mdi mdi-settings mr-2 text-gray"></i> Changer mot de passe
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" routerLinkActive="active-menu-link-user"
|
||||||
|
[routerLink]="['/administration/dashbord']">
|
||||||
|
<i class="mdi mdi-chart-line mr-2 text-gray"></i> Tableau de bord
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" (click)="singOut()">
|
||||||
|
<i class="mdi mdi-close mr-2 text-gray"></i> Déconnexion
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="navbar-toggler navbar-toggler-right d-lg-none align-self-center" type="button"
|
||||||
|
data-toggle="offcanvas">
|
||||||
|
<span class="mdi mdi-menu"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<!-- partial -->
|
||||||
|
|
||||||
|
<div class="row" style="background-color: rgb(20 97 59);margin-top: 75px;">
|
||||||
|
<div class="col-md-5" style="padding: 25px;">
|
||||||
|
<h3 class="text-secondary ml-5"
|
||||||
|
style="font-size: 11px;text-transform: uppercase;margin-top: 2%;color: rgba(255, 255, 255, 0.61) !important;">
|
||||||
|
Bienvenue dans votre espace personnel sécurisé</h3>
|
||||||
|
<h1 class="text-white ml-5" style="font-size: 11px;line-height: 1.5;">
|
||||||
|
Utilisateur connecté : DÉVELOPPEMENT ADMIN<br>
|
||||||
|
Fonction sélectionnée : Développement UNIQUEMENT - Administrateur technique</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2" style=" padding-left: 3.1%;">
|
||||||
|
<h3 class="text-secondary"
|
||||||
|
style="font-size: 10px;text-transform: uppercase;margin-top: 8%;color: rgba(255, 255, 255, 0.61) !important;">
|
||||||
|
Dèrnières connexions</h3>
|
||||||
|
<span class="text-white" style="font-size: 10px;display:block;line-height: 1.5;">23h22 lundi 26 janvier
|
||||||
|
2026</span>
|
||||||
|
<span class="text-white" style="font-size: 10px;display:block;line-height: 1.5;">23h22 lundi 26 janvier
|
||||||
|
2026</span>
|
||||||
|
<span class="text-white" style="font-size: 10px;display:block;line-height: 1.5;">23h22 lundi 26 janvier
|
||||||
|
2026</span>
|
||||||
|
<span class="text-white" style="font-size: 10px;display:block;line-height: 1.5;">23h22 lundi 26 janvier
|
||||||
|
2026</span>
|
||||||
|
<span class="text-white" style="font-size: 10px;display:block;line-height: 1.5;">23h22 lundi 26 janvier
|
||||||
|
2026</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" style="background: #f5f5f5ff;">
|
||||||
|
<div class="col-md-12" style="padding: 2% 5%;display: flex;">
|
||||||
|
<input type="text" class="search-menu-dash mt-2 mb-2"
|
||||||
|
placeholder="Rechercher un contribuable pour accéder à son dossier ..." nz-popover nzPopoverTrigger="click"
|
||||||
|
[nzPopoverContent]="contentTemplateSearch" nzPopoverPlacement="leftBottom">
|
||||||
|
<button class="btn-search-input">
|
||||||
|
<img src="assets/sigibe/search-white.svg" alt="">
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ng-template #contentTemplateSearch>
|
||||||
|
<strong> Tapez au moins 3 caractères. </strong>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--<div class="row" style="background: #0d633c59;">-->
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!--<div class="col-md-2 center-vertical text-center">
|
||||||
|
<img src="assets/static/fond_carte_login.png" alt="" class="img-left-menu">
|
||||||
|
</div>-->
|
||||||
|
<div class="col-md-12" style="padding: 2% 5%;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 p-5">
|
||||||
|
<h1 class="titrePageDashboard"> Mon tableau de bord </h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 div4-menu-dashboard" *ngFor="let item of moduleListDash()"
|
||||||
|
(click)="selectModuleAccess(item)">
|
||||||
|
<div class="p-2 {{ item.classIconBody }}"
|
||||||
|
[ngStyle]="{ backgroundColor: item ? item.color : '' }">
|
||||||
|
<img src="assets/sigibe/{{ item.icone }}" alt="" style="height: 20px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style="font-size: 12px;margin-left: 10px;color: #3cb22a;;"> {{ item.title }} </h3>
|
||||||
|
<h6 style="font-size: 13px;margin-left: 10px;color: #333;font-weight: 900;">
|
||||||
|
{{ item.description }} </h6>
|
||||||
|
<p style="font-size: 11px;margin-left: 10px;color: rgb(99, 99, 99);"> {{ item.detail }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--<div class="col-md-4" style="display: flex;">
|
||||||
|
<div class="p-2 div-icon-dash" style="background: rgb(44 44 44) !important">
|
||||||
|
<img src="assets/sigibe/team.svg" alt="" style="height: 20px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style="font-size: 12px;margin-left: 10px;color: #3cb22a;;"> Tableau de bord </h3>
|
||||||
|
<h6 style="font-size: 13px;margin-left: 10px;color: #333;font-weight: 900;"> Suivi global
|
||||||
|
des enquêtes foncières fiscales </h6>
|
||||||
|
<p style="font-size: 11px;margin-left: 10px;color: rgb(99, 99, 99);"> Tableau de bord pour
|
||||||
|
suivre l'avancement des enquêtes fiscales. </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4" style="display: flex;">
|
||||||
|
<div class="p-2 div-icon-dash" style="background: rgb(44 44 44) !important">
|
||||||
|
<img src="assets/sigibe/team.svg" alt="" style="height: 20px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style="font-size: 12px;margin-left: 10px;color: #3cb22a;"> Tableau de bord </h3>
|
||||||
|
<h6 style="font-size: 13px;margin-left: 10px;color: #333;font-weight: 900;"> Suivi de
|
||||||
|
l'exploitation </h6>
|
||||||
|
<p style="font-size: 11px;margin-left: 10px;color: rgb(99, 99, 99);"> Tableau de bord pour
|
||||||
|
suivre l'exploitation des données fiscales. </p>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 p-5">
|
||||||
|
<h1 class="titrePageDashboard"> Accès dossiers en cours </h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 div4-menu-dashboard mb-5" *ngFor="let item of moduleListExploitation()"
|
||||||
|
(click)="selectModuleAccess(item)">
|
||||||
|
<div class="p-2 {{ item.classIconBody }}"
|
||||||
|
[ngStyle]="{ backgroundColor: item && item.color ? item.color : '' }">
|
||||||
|
<img src="assets/sigibe/{{ item.icone }}" alt="" style="height: 20px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 style="font-size: 12px;margin-left: 10px;color: #3cb22a;"> {{ item.title }} </h3>
|
||||||
|
<h6 style="font-size: 13px;margin-left: 10px;color: #333;font-weight: 900;">
|
||||||
|
{{ item.description }} </h6>
|
||||||
|
<p style="font-size: 11px;margin-left: 10px;color: rgb(99, 99, 99);"> {{ item.detail }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" style="margin-top: -3% !important;">
|
||||||
|
<div class="col-md-12 p-5">
|
||||||
|
<h1 class="titrePageDashboard"> Accès rapide </h1>
|
||||||
|
|
||||||
|
<div class="formulaire">
|
||||||
|
<div>
|
||||||
|
<h2 style="font-size: 16px;">Accès rapide à tous les dossiers</h2>
|
||||||
|
<p style="font-size: 11px;color: rgb(99, 99, 99);">Saisissez la référence d'un dossier et
|
||||||
|
cliquez sur le bouton "Rechercher". Vous serez
|
||||||
|
redirigé vers un autre dossier du même type.</p>
|
||||||
|
</div>
|
||||||
|
<form [formGroup]="rechercheForm" *ngIf="rechercheForm" style="width: 100%;">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 mt-3">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner le critère de recherche" formControlName="type">
|
||||||
|
<nz-option *ngFor="let item of typeList" [nzLabel]="item.nom"
|
||||||
|
[nzValue]="item.value"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -10px;"> Critère de recherche
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 mt-3">
|
||||||
|
<div class="did-floating-label-content me-1">
|
||||||
|
<input class="did-floating-input" type="text" id="reference"
|
||||||
|
formControlName="reference" placeholder="">
|
||||||
|
<label class="did-floating-label"> Saisissez la référence du dossier à consulter
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<button class="btn btn-primary btn-fw btn-select-module"
|
||||||
|
style="height: 40px;background: #2c2c2c;border: none;float: right;">
|
||||||
|
<span> Rechercher </span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<app-footer></app-footer>
|
||||||
136
src/app/manage/manage.component.ts
Normal file
136
src/app/manage/manage.component.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { Component, ChangeDetectorRef, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { GlobalService } from '../global.service';
|
||||||
|
import { TokenStorage } from '../utilitaire/token-storage';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from '../crud.service';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { link } from 'fs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-manage',
|
||||||
|
templateUrl: './manage.component.html',
|
||||||
|
styleUrls: ['./manage.component.css']
|
||||||
|
})
|
||||||
|
export class ManageComponent implements OnInit {
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
enqueteList: any[] = [];
|
||||||
|
commentaireList: any[] = [];
|
||||||
|
|
||||||
|
enqueteCheckList: any[] = [];
|
||||||
|
|
||||||
|
rechercheForm?: FormGroup;
|
||||||
|
|
||||||
|
typeList = [
|
||||||
|
{ nom: 'Numéro NPI du contribuable', value: 'npi' },
|
||||||
|
{ nom: 'Numéro IFU du contribuable', value: 'ifu' },
|
||||||
|
{ nom: 'Numéro du titre foncier de la parcelle', value: 'tf' },
|
||||||
|
{ nom: 'Numéro de la parcelle au cadastre', value: 'nup' },
|
||||||
|
{ nom: 'Numéro provisoire de la parcelle au cadastre', value: 'nupp' },
|
||||||
|
{ nom: 'Quartier, Ilot, Parcelle', value: 'qip' },
|
||||||
|
];
|
||||||
|
|
||||||
|
visible: boolean = false;
|
||||||
|
|
||||||
|
moduleList: any[] = [];
|
||||||
|
|
||||||
|
currentModule: any = null;
|
||||||
|
|
||||||
|
notifEnqueteList: any = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private cdref: ChangeDetectorRef,
|
||||||
|
private router: Router,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private fb: FormBuilder,
|
||||||
|
) {
|
||||||
|
this.currentModule = this.tokenStorage.getModule();
|
||||||
|
|
||||||
|
console.log('this.currentModule ==>', this.currentModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.crudService.getAll(
|
||||||
|
`statistique/nombre-enquete/par-objet/en-cours`
|
||||||
|
).subscribe({
|
||||||
|
next: (data: any) => {
|
||||||
|
console.log('notif ===>', data);
|
||||||
|
this.notifEnqueteList = data && data.object ? data.object : null;
|
||||||
|
}});
|
||||||
|
this.moduleList = this.globalService.getModuleList();
|
||||||
|
|
||||||
|
this.rechercheForm = this.fb.group({
|
||||||
|
type: [null, [Validators.required]],
|
||||||
|
reference: [null]
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
|
||||||
|
this.globalService.getEnqueteCheckData().subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.enqueteCheckList = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalNotification(): number {
|
||||||
|
if(this.notifEnqueteList)
|
||||||
|
return (this.notifEnqueteList.nombreEnqueteBatiment + this.notifEnqueteList.nombreEnqueteParcelle + this.notifEnqueteList.nombreEnqueteUniteLogement);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
singOut(): void {
|
||||||
|
this.tokenStorage.signOut();
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto(url: string): void {
|
||||||
|
this.router.navigate([url]);
|
||||||
|
}
|
||||||
|
|
||||||
|
isOneRoles(param: any): boolean {
|
||||||
|
if (this.user != null) {
|
||||||
|
return this.user.roles ? this.user.roles.indexOf(param) > -1 : false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRoles(params: any[]): boolean {
|
||||||
|
if (this.user != null) {
|
||||||
|
return params.indexOf(this.user.roles[0]?.nom) > -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickMe(): void {
|
||||||
|
this.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
change(value: boolean): void {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectModuleAccess(module: any): void {
|
||||||
|
console.log('module ==>', module);
|
||||||
|
this.tokenStorage.saveModule(JSON.stringify(module));
|
||||||
|
this.router.navigate([module.link]);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleListDash(): any[] {
|
||||||
|
return this.moduleList.filter(m => m.dash == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleListExploitation(): any[] {
|
||||||
|
return this.moduleList.filter(m => m.dash == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
23
src/app/manage/manage.module.ts
Normal file
23
src/app/manage/manage.module.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { ManageRoutingModule } from './manage-routing.module';
|
||||||
|
import { ManageComponent } from './manage.component';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ManageComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ManageRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
SharedModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ManageModule { }
|
||||||
5
src/app/models/login-request.ts
Executable file
5
src/app/models/login-request.ts
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
export class LoginRequest {
|
||||||
|
password!: string;
|
||||||
|
username!: string;
|
||||||
|
}
|
||||||
|
|
||||||
5
src/app/models/role.ts
Executable file
5
src/app/models/role.ts
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
export class Role {
|
||||||
|
id?: number;
|
||||||
|
name?: string;
|
||||||
|
libelle?: string;
|
||||||
|
}
|
||||||
19
src/app/models/user.ts
Executable file
19
src/app/models/user.ts
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Role } from './role';
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
email: string;
|
||||||
|
id: number;
|
||||||
|
nom: string;
|
||||||
|
prenom: string;
|
||||||
|
roles: Role[];
|
||||||
|
username: string;
|
||||||
|
profil: string;
|
||||||
|
tel: string;
|
||||||
|
role: string;
|
||||||
|
|
||||||
|
createdAt: string;
|
||||||
|
estSupprimer: boolean;
|
||||||
|
updatedAt: string;
|
||||||
|
createdBy: number;
|
||||||
|
updatedBy: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<div class="row" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">BLOCS</h4>
|
||||||
|
<p class="card-description">
|
||||||
|
Liste des différents blocs (découpage des zones) assignés
|
||||||
|
</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped" datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Code
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Nom
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Secteur
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Découpage
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of blocsList; let i=index" class="border-bottom-light">
|
||||||
|
<td style="padding: 15px !important;">
|
||||||
|
{{ todo.cote }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.nom }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.secteur.nom }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.arrondissement && todo.arrondissement.commune ? todo.arrondissement.commune.nom : '-' }} / {{ todo.arrondissement ? todo.arrondissement.nom : '-' }} {{ todo.quartier ? ' / '+ todo.quartier.nom : '' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { Component, ChangeDetectionStrategy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { DataTableDirective } from 'angular-datatables';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-bloc-by-structure',
|
||||||
|
templateUrl: './bloc-by-structure.component.html',
|
||||||
|
styleUrls: ['./bloc-by-structure.component.css']
|
||||||
|
})
|
||||||
|
export class BlocByStructureComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(DataTableDirective, { static: false })
|
||||||
|
dtElement?: DataTableDirective;
|
||||||
|
dtOptions: DataTables.Settings = {};
|
||||||
|
dtTrigger: Subject<any> = new Subject<any>();
|
||||||
|
|
||||||
|
blocsList: any[] = [];
|
||||||
|
|
||||||
|
isActionInProgress: boolean = false;
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private crudService: CrudService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
this.dtOptions = {
|
||||||
|
pagingType: 'full_numbers',
|
||||||
|
pageLength: 10,
|
||||||
|
processing: true,
|
||||||
|
language: {
|
||||||
|
search: "Rechercher :",
|
||||||
|
emptyTable: "Aucune donnée disponible",
|
||||||
|
lengthMenu: "Afficher _MENU_ éléments",
|
||||||
|
info: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||||
|
infoEmpty: "Affichage de l'élement 0 à 0 sur 0 éléments",
|
||||||
|
infoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||||
|
paginate: {
|
||||||
|
first: "<i class='menu-icon mdi mdi-chevron-double-left'></i>",
|
||||||
|
previous: "<i class='menu-icon mdi mdi-chevron-left'></i>",
|
||||||
|
next: "<i class='menu-icon mdi mdi-chevron-right'></i>",
|
||||||
|
last: "<i class='menu-icon mdi mdi-chevron-double-right'></i>"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.dtTrigger.next(this.dtOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.dtTrigger.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): void {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.blocsList = [];
|
||||||
|
this.refreshDataTable();
|
||||||
|
if(this.user && this.user.structure) {
|
||||||
|
this.crudService.getAll('bloc/list-by-structure?idStructure='+this.user.structure?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.blocsList = data != null ? data.object : [];
|
||||||
|
this.blocsList = [...this.blocsList];
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshDataTable(): void {
|
||||||
|
this.dtElement?.dtInstance.then((dtInstance: DataTables.Api) => {
|
||||||
|
// Destroy the table first
|
||||||
|
dtInstance.destroy();
|
||||||
|
// Call the dtTrigger to rerender again
|
||||||
|
this.dtTrigger.next(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
src/app/office/bloc-quartier/bloc-quartier-routing.module.ts
Normal file
15
src/app/office/bloc-quartier/bloc-quartier-routing.module.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { BlocQuartierComponent } from './bloc-quartier.component';
|
||||||
|
import { BlocByStructureComponent } from './bloc-by-structure/bloc-by-structure.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: 'gestion-bloc', component: BlocQuartierComponent },
|
||||||
|
{ path: 'bloc-by-structure', component: BlocByStructureComponent }
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class BlocQuartierRoutingModule { }
|
||||||
184
src/app/office/bloc-quartier/bloc-quartier.component.html
Normal file
184
src/app/office/bloc-quartier/bloc-quartier.component.html
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
|
||||||
|
<div class="row" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card" *ngIf="isForm == true">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">Formulaire</h4>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<form [formGroup]="blocForm" *ngIf="blocForm">
|
||||||
|
<div class="row">
|
||||||
|
<!--<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<input class="did-floating-input" type="text" id="cote"
|
||||||
|
formControlName="cote" placeholder="">
|
||||||
|
<label class="did-floating-label"> Code du bloc <span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner le secteur"
|
||||||
|
formControlName="secteur" [compareWith]="compareFn"
|
||||||
|
(ngModelChange)="checkSecteurDecoupageBySecteur($event)">
|
||||||
|
<nz-option *ngFor="let item of secteurList" [nzLabel]="item.nom"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Secteur d'intervention
|
||||||
|
<span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<input class="did-floating-input" type="text" id="nom"
|
||||||
|
formControlName="nom" placeholder="">
|
||||||
|
<label class="did-floating-label"> Nom du bloc <span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--<div class="col-lg-4">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner le quartier"
|
||||||
|
formControlName="quartier" [compareWith]="compareFn">
|
||||||
|
<nz-option *ngFor="let item of quartierList" [nzLabel]="item.nom"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Quartier </label>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
</div>
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="did-floating-label-content">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner le découpage"
|
||||||
|
formControlName="secteurDecoupage" [compareWith]="compareFn"
|
||||||
|
(ngModelChange)="checkSecteurDecoupage($event)">
|
||||||
|
<nz-option *ngFor="let item of secteurDecoupageList" [nzLabel]="(item.arrondissement ? item.arrondissement.commune.nom +' / ' : '') + ' '+ item.arrondissement.nom + (item.quartier ? ' / '+ item.quartier.nom : '')"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Découpage territorial </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-secondary btn-fw mr-2"
|
||||||
|
(click)="showHideForm(false)" [disabled]="isActionInProgress">
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary btn-fw" (click)="saveForm()"
|
||||||
|
[disabled]="isActionInProgress && arrondissementPaylod == null">
|
||||||
|
{{ isActionInProgress == false ? 'Enregistrer' : 'Opération en cours ...' }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<!--<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner la commune" (ngModelChange)="filterArrondissementByCommune($event)"
|
||||||
|
[(ngModel)]="communePaylod" [compareWith]="compareFn">
|
||||||
|
<nz-option *ngFor="let item of communeList" [nzLabel]="item.nom" [nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Commune <span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<!--<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner l'arrondissement" (ngModelChange)="listQuartierByArrondissement($event)"
|
||||||
|
[(ngModel)]="arrondissementPaylod" [compareWith]="compareFn">
|
||||||
|
<nz-option *ngFor="let item of arrondissementList" [nzLabel]="item.nom" [nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Arrondissement <span class="text-danger"> *</span> </label>
|
||||||
|
</div>-->
|
||||||
|
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner le centre d'impôt"
|
||||||
|
(ngModelChange)="listBlocsAndSecteurByStructure($event)" [(ngModel)]="structurePaylod"
|
||||||
|
[compareWith]="compareFn">
|
||||||
|
<nz-option *ngFor="let item of structureList" [nzLabel]="item.nom"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Centre d'impôt
|
||||||
|
<span class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">BLOCS</h4>
|
||||||
|
<p class="card-description">
|
||||||
|
Liste des différents blocs (découpage des zones) pour la gestion des enquêtes
|
||||||
|
</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped"
|
||||||
|
datatable [dtOptions]="dtOptions"
|
||||||
|
[dtTrigger]="dtTrigger">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Code
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Nom
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Secteur
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Découpage
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
<button type="button" class="btn btn-icons btn-rounded btn-primary mr-1"
|
||||||
|
[disabled]="structurePaylod == null"
|
||||||
|
(click)="showHideForm(true);makeForm(null)">
|
||||||
|
<i class="mdi mdi-plus btn-icon-modify"></i>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of blocList; let i=index" class="border-bottom-light">
|
||||||
|
<td>
|
||||||
|
{{ todo.coteq ? todo.coteq : todo.cote }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.nom }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.secteur ? todo.secteur.nom : '-' }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.arrondissement && todo.arrondissement.commune ? todo.arrondissement.commune.nom : '-' }} / {{ todo.arrondissement ? todo.arrondissement.nom : '-' }} {{ todo.quartier ? ' / '+ todo.quartier.nom : '' }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!--<td class="text-center">
|
||||||
|
<label class="badge badge-info" *ngIf="todo.quartiers.length > 0"> {{ todo.quartiers.length }}</label>
|
||||||
|
<label class="badge badge-danger" *ngIf="todo.quartiers.length == 0"> {{ todo.quartiers.length }}</label>
|
||||||
|
</td>-->
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="btn btn-icons btn-rounded btn-success mr-1"
|
||||||
|
(click)="makeForm(todo)">
|
||||||
|
<i class="mdi mdi-pencil btn-icon-modify"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-icons btn-rounded btn-danger"
|
||||||
|
(click)="showDeleteConfirm(todo, i)">
|
||||||
|
<i class="mdi mdi-delete btn-icon-modify"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
367
src/app/office/bloc-quartier/bloc-quartier.component.ts
Normal file
367
src/app/office/bloc-quartier/bloc-quartier.component.ts
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
import { Component, ChangeDetectionStrategy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { DataTableDirective } from 'angular-datatables';
|
||||||
|
import { forkJoin, Subject } from 'rxjs';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
|
||||||
|
export interface Bloc {
|
||||||
|
id: number;
|
||||||
|
cote: string;
|
||||||
|
nom: string;
|
||||||
|
arrondissement: any;
|
||||||
|
structure: any;
|
||||||
|
quartier: any;
|
||||||
|
secteurDecoupage: any;
|
||||||
|
secteur: any;
|
||||||
|
quartiers: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-bloc-quartier',
|
||||||
|
templateUrl: './bloc-quartier.component.html',
|
||||||
|
styleUrls: ['./bloc-quartier.component.css']
|
||||||
|
})
|
||||||
|
export class BlocQuartierComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild(DataTableDirective, { static: false })
|
||||||
|
dtElement?: DataTableDirective;
|
||||||
|
dtOptions: DataTables.Settings = {};
|
||||||
|
dtTrigger: Subject<any> = new Subject<any>();
|
||||||
|
|
||||||
|
isForm = false;
|
||||||
|
quartierList: any[] = [];
|
||||||
|
arrondissementList: any[] = [];
|
||||||
|
arrondissementFilteredList: any[] = [];
|
||||||
|
communeList: any[] = [];
|
||||||
|
structureList: any[] = [];
|
||||||
|
secteurList: any[] = [];
|
||||||
|
secteurDecoupageList: any[] = [];
|
||||||
|
|
||||||
|
blocList: any[] = [];
|
||||||
|
|
||||||
|
arrondissementPaylod: any = null;
|
||||||
|
communePaylod: any = null;
|
||||||
|
structurePaylod: any = null;
|
||||||
|
|
||||||
|
blocForm?: FormGroup;
|
||||||
|
isActionInProgress: boolean = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
this.dtOptions = {
|
||||||
|
pagingType: 'full_numbers',
|
||||||
|
pageLength: 10,
|
||||||
|
processing: true,
|
||||||
|
language: {
|
||||||
|
search: "Rechercher :",
|
||||||
|
emptyTable: "Aucune donnée disponible",
|
||||||
|
lengthMenu: "Afficher _MENU_ éléments",
|
||||||
|
info: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||||
|
infoEmpty: "Affichage de l'élement 0 à 0 sur 0 éléments",
|
||||||
|
infoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||||
|
paginate: {
|
||||||
|
first: "<i class='menu-icon mdi mdi-chevron-double-left'></i>",
|
||||||
|
previous: "<i class='menu-icon mdi mdi-chevron-left'></i>",
|
||||||
|
next: "<i class='menu-icon mdi mdi-chevron-right'></i>",
|
||||||
|
last: "<i class='menu-icon mdi mdi-chevron-double-right'></i>"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.makeForm(null);
|
||||||
|
this.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.dtTrigger.next(this.dtOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
compareFn = (o1: any, o2: any) => (o1 && o2 ? o1.id === o2.id : o1 === o2);
|
||||||
|
|
||||||
|
makeForm(bloc: Bloc | null): void {
|
||||||
|
this.blocForm = this.fb.group({
|
||||||
|
id: [bloc != null ? bloc.id : null],
|
||||||
|
cote: [bloc != null ? bloc.cote : null],
|
||||||
|
nom: [bloc != null ? bloc.nom : null,
|
||||||
|
[Validators.required]],
|
||||||
|
arrondissement: [bloc != null ? bloc.arrondissement : null],
|
||||||
|
quartier: [bloc != null ? bloc.quartier : null],
|
||||||
|
structure: [bloc != null ? bloc.structure : null],
|
||||||
|
secteur: [bloc != null ? bloc.secteur : null, [Validators.required]],
|
||||||
|
secteurDecoupage: [bloc != null ? bloc.secteurDecoupage : null],
|
||||||
|
quartiers: [bloc != null ? bloc.quartiers : []],
|
||||||
|
});
|
||||||
|
if (bloc != null) {
|
||||||
|
this.showHideForm(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetForm(e: MouseEvent): void {
|
||||||
|
e.preventDefault();
|
||||||
|
this.blocForm?.reset();
|
||||||
|
for (const key in this.blocForm?.controls) {
|
||||||
|
this.blocForm?.controls[key].markAsPristine();
|
||||||
|
this.blocForm?.controls[key].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
this.makeForm(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.dtTrigger.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSecteurDecoupageBySecteur(value: any): void {
|
||||||
|
this.secteurDecoupageList = [];
|
||||||
|
if(value) {
|
||||||
|
this.secteurDecoupageList = value.secteurDecoupages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkSecteurDecoupage(value: any): void {
|
||||||
|
this.blocForm?.get('arrondissement')?.setValue(null);
|
||||||
|
this.blocForm?.get('quartier')?.setValue(null);
|
||||||
|
if(value) {
|
||||||
|
this.blocForm?.get('arrondissement')?.setValue(value.arrondissement);
|
||||||
|
this.blocForm?.get('quartier')?.setValue(value.quartier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): void {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
/*this.communeList = [];
|
||||||
|
this.arrondissementList = [];*/
|
||||||
|
this.structureList = [];
|
||||||
|
|
||||||
|
//const $quartiers = this.crudService.getAll('quartier/all');
|
||||||
|
//const $communes = this.crudService.getAll('commune/all');
|
||||||
|
//const $arrondissements = this.crudService.getAll('arrondissement/commune/58');
|
||||||
|
const $structures = this.crudService.getAll('structure/all');
|
||||||
|
|
||||||
|
forkJoin([$structures]).subscribe(([structures]) => {
|
||||||
|
// All data available
|
||||||
|
console.log(structures);
|
||||||
|
//console.log(communes);
|
||||||
|
//const dataCommune: any = communes;
|
||||||
|
const dataStructures: any = structures;
|
||||||
|
|
||||||
|
this.structureList = dataStructures.object;
|
||||||
|
//this.communeList = dataCommune.object;
|
||||||
|
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*filterArrondissementByCommune(value: any): void {
|
||||||
|
console.log('value ==> ', value)
|
||||||
|
this.communePaylod = value;
|
||||||
|
this.listQuartierByArrondissement(null);
|
||||||
|
this.arrondissementFilteredList = [];
|
||||||
|
if (this.communePaylod != null) {
|
||||||
|
this.arrondissementFilteredList = this.arrondissementList.filter((element) => element.commune != null && element.commune.id == this.communePaylod.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listQuartierByArrondissement(value: any): void {
|
||||||
|
this.arrondissementPaylod = value;
|
||||||
|
this.quartierList = [];
|
||||||
|
this.structureList = [];
|
||||||
|
this.blocList = [];
|
||||||
|
this.refreshDataTable();
|
||||||
|
if (this.arrondissementPaylod != null) {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
const $quartiers = this.crudService.getAll('quartier/arrondissement/' + this.arrondissementPaylod?.id);
|
||||||
|
const $blocs = this.crudService.getAll('bloc/list-by-arrondissement?idArrondissement='+ this.arrondissementPaylod?.id);
|
||||||
|
const $structures = this.crudService.getAll('structure/all-by-arrondissement?arrondissementId='+ this.arrondissementPaylod?.id);
|
||||||
|
|
||||||
|
forkJoin([$quartiers, $blocs, $structures]).subscribe(([quartiers, blocs, structures]) => {
|
||||||
|
// All data available
|
||||||
|
console.log(quartiers);
|
||||||
|
console.log(blocs);
|
||||||
|
const dataQuartier: any = quartiers;
|
||||||
|
const dataBlocs: any = blocs;
|
||||||
|
const dataStructures: any = structures;
|
||||||
|
|
||||||
|
this.quartierList = dataQuartier.object;
|
||||||
|
this.blocList = dataBlocs.object;
|
||||||
|
this.structureList = dataStructures.object;
|
||||||
|
|
||||||
|
this.quartierList = [...this.quartierList];
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
listBlocsAndSecteurByStructure(value: any): void {
|
||||||
|
this.structurePaylod = value;
|
||||||
|
console.log("value ===> ", value);
|
||||||
|
this.secteurList = [];
|
||||||
|
this.secteurDecoupageList = [];
|
||||||
|
this.blocList = [];
|
||||||
|
this.blocForm?.reset();
|
||||||
|
this.refreshDataTable();
|
||||||
|
if (this.structurePaylod != null) {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.crudService.getAll('secteur/by-structure-id/' + this.structurePaylod?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.secteurList = data != null ? data.object : [];
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const $blocs = this.crudService.getAll('bloc/list-by-structure?idStructure='+ this.structurePaylod?.id);
|
||||||
|
|
||||||
|
forkJoin([$blocs]).subscribe(([blocs]) => {
|
||||||
|
// All data available
|
||||||
|
console.log(blocs);
|
||||||
|
const dataBlocs: any = blocs;
|
||||||
|
|
||||||
|
this.blocList = dataBlocs.object;
|
||||||
|
|
||||||
|
this.blocList = [...this.blocList];
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshDataTable(): void {
|
||||||
|
this.dtElement?.dtInstance.then((dtInstance: DataTables.Api) => {
|
||||||
|
// Destroy the table first
|
||||||
|
dtInstance.destroy();
|
||||||
|
// Call the dtTrigger to rerender again
|
||||||
|
this.dtTrigger.next(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showDeleteConfirm(element: any, index: number): void {
|
||||||
|
this.modal.confirm({
|
||||||
|
nzTitle: 'Confirmez-vous ?',
|
||||||
|
nzContent: 'suppression de <strong style="color: red;">' + element.nom + '</strong>',
|
||||||
|
nzOkText: 'Oui',
|
||||||
|
nzOkType: 'primary',
|
||||||
|
nzOkDanger: true,
|
||||||
|
nzOnOk: () => {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.crudService.deleteElement('bloc/delete', element.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.quartierList.splice(index, 1);
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.message.create('success', `Suppression effectuée avec succès.`);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nzCancelText: 'Non',
|
||||||
|
nzOnCancel: () => console.log('Cancel')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showHideForm(value: boolean): void {
|
||||||
|
this.isForm = value;
|
||||||
|
if (!value) {
|
||||||
|
this.makeForm(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveForm(): void {
|
||||||
|
for (const i in this.blocForm?.controls) {
|
||||||
|
this.blocForm?.controls[i].markAsDirty();
|
||||||
|
this.blocForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.blocForm?.valid) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
const formData = this.blocForm?.value;
|
||||||
|
formData.structure = this.structurePaylod;
|
||||||
|
if (
|
||||||
|
formData.id == null ||
|
||||||
|
formData.id == undefined ||
|
||||||
|
formData.id == ''
|
||||||
|
) {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.crudService.save('bloc/create', formData).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.blocList.unshift(data.object);
|
||||||
|
this.message.create('success', `Enregistrement effectué avec succès.`);
|
||||||
|
this.makeForm(null);
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const i = this.blocList.findIndex(
|
||||||
|
(element) => element.id == formData.id
|
||||||
|
);
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.crudService.update('bloc/update', formData).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.blocList.splice(i, 1);
|
||||||
|
this.blocList.unshift(data.object);
|
||||||
|
this.message.create('success', `Modification effectuée avec succès.`);
|
||||||
|
this.makeForm(null);
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.showHideForm(false);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: 'Formulaire invalid. Un ou plusieurs champs sont vides...'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
src/app/office/bloc-quartier/bloc-quartier.module.ts
Normal file
35
src/app/office/bloc-quartier/bloc-quartier.module.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { DataTablesModule } from 'angular-datatables';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { NzDividerModule } from 'ng-zorro-antd/divider';
|
||||||
|
import { NzButtonModule } from 'ng-zorro-antd/button';
|
||||||
|
import { NzSelectModule } from 'ng-zorro-antd/select';
|
||||||
|
|
||||||
|
import { BlocQuartierRoutingModule } from './bloc-quartier-routing.module';
|
||||||
|
import { BlocQuartierComponent } from './bloc-quartier.component';
|
||||||
|
import { BlocByStructureComponent } from './bloc-by-structure/bloc-by-structure.component';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
BlocQuartierComponent,
|
||||||
|
BlocByStructureComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
BlocQuartierRoutingModule,
|
||||||
|
|
||||||
|
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
NzDividerModule,
|
||||||
|
NzButtonModule,
|
||||||
|
NzSelectModule,
|
||||||
|
|
||||||
|
DataTablesModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class BlocQuartierModule { }
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
|||||||
|
<div class="row" [ngStyle]="{ backgroundColor: module ? module.color : '' }" id="formulaire">
|
||||||
|
<div class="col-md-5" style="padding: 35px;margin-bottom: 2%;">
|
||||||
|
<div style="margin-left: 16%;">
|
||||||
|
<h3 class="text-secondary"
|
||||||
|
style="font-size: 11px;text-transform: uppercase;color: rgba(255, 255, 255, 0.61) !important;">
|
||||||
|
Module Cartographie </h3>
|
||||||
|
<h1 class="text-white" style="font-size: 16px;line-height: 1.5;">
|
||||||
|
Dossier en cours sur le module cartographie</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5" style="padding-left: 3.1%;">
|
||||||
|
<img src="assets/sigibe/logo-mef-white.png" alt=""
|
||||||
|
style="width: 200px;margin-top: 4%;float: right;margin-right: 20%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-left: 6%;margin-right: 6%;background-color: rgb(255 255 255 / 75%);">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-cartographie-router',
|
||||||
|
templateUrl: './cartographie-router.component.html',
|
||||||
|
styleUrls: ['./cartographie-router.component.css']
|
||||||
|
})
|
||||||
|
export class CartographieRouterComponent {
|
||||||
|
|
||||||
|
module: any = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.module = JSON.parse(this.tokenStorage.getModule());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
src/app/office/cartographie/cartographie-routing.module.ts
Normal file
58
src/app/office/cartographie/cartographie-routing.module.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { CartographieComponent } from './cartographie.component';
|
||||||
|
import { ChargerFichierComponent } from './charger-fichier/charger-fichier.component';
|
||||||
|
import { DetailByNupGeomComponent } from './detail-by-nup-geom/detail-by-nup-geom.component';
|
||||||
|
import { CartographieFiscaleTfuComponent } from './cartographie-fiscale-tfu/cartographie-fiscale-tfu.component';
|
||||||
|
import { SommaireCartographieComponent } from './sommaire-cartographie/sommaire-cartographie.component';
|
||||||
|
import { NotFoundComponent } from 'src/app/shared/not-found/not-found.component';
|
||||||
|
import { CartographieRouterComponent } from './cartographie-router/cartographie-router.component';
|
||||||
|
import { QuartierComponent } from '../reference/quartier/quartier.component';
|
||||||
|
import { ExerciceComponent } from '../reference/exercice/exercice.component';
|
||||||
|
import { ZoneRfuComponent } from '../reference/zone-rfu/zone-rfu.component';
|
||||||
|
import { DepartementComponent } from '../reference/departement/departement.component';
|
||||||
|
import { CommuneComponent } from '../reference/commune/commune.component';
|
||||||
|
import { ArrondissementComponent } from '../reference/arrondissement/arrondissement.component';
|
||||||
|
import { UsageComponent } from '../reference/usage/usage.component';
|
||||||
|
import { CategorieBatimentComponent } from '../reference/categorie-batiment/categorie-batiment.component';
|
||||||
|
import { DetailInformationParcelleComponent } from 'src/app/shared/detail-information-parcelle/detail-information-parcelle.component';
|
||||||
|
import { DetailInformationBatimentComponent } from 'src/app/shared/detail-information-batiment/detail-information-batiment.component';
|
||||||
|
import { DetailInformationUniteLogementComponent } from 'src/app/shared/detail-information-unite-logement/detail-information-unite-logement.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '', component: CartographieComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'data', component: CartographieRouterComponent,
|
||||||
|
children: [
|
||||||
|
{ path: 'reference/quartier', component: QuartierComponent },
|
||||||
|
{ path: 'reference/exercice', component: ExerciceComponent },
|
||||||
|
|
||||||
|
{ path: 'reference/zone-rfu', component: ZoneRfuComponent },
|
||||||
|
{ path: 'reference/departement', component: DepartementComponent },
|
||||||
|
{ path: 'reference/commune', component: CommuneComponent },
|
||||||
|
{ path: 'reference/arrondissement', component: ArrondissementComponent },
|
||||||
|
{ path: 'reference/usage', component: UsageComponent },
|
||||||
|
{ path: 'reference/categorie-batiment', component: CategorieBatimentComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'fiche-chargement-geojson', component: ChargerFichierComponent },
|
||||||
|
{ path: 'sommaire-cartographie', component: SommaireCartographieComponent },
|
||||||
|
{ path: 'detail-parcelle/:id', component: DetailInformationParcelleComponent },
|
||||||
|
{ path: 'detail-batiment/:id', component: DetailInformationBatimentComponent },
|
||||||
|
{ path: 'detail-unite-logement/:id', component: DetailInformationUniteLogementComponent },
|
||||||
|
|
||||||
|
{ path: '**', component: NotFoundComponent }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ path: 'cartographie-fiscale-tfu', component: CartographieFiscaleTfuComponent },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class CartographieRoutingModule { }
|
||||||
83
src/app/office/cartographie/cartographie.component.css
Normal file
83
src/app/office/cartographie/cartographie.component.css
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
.badge-tree-display {
|
||||||
|
float: right;
|
||||||
|
margin-right: 35px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: -28px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-tree-bloc {
|
||||||
|
float: right;
|
||||||
|
margin-right: 35px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boutton-enquete-action-green {
|
||||||
|
cursor: pointer;
|
||||||
|
background: green;
|
||||||
|
padding: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boutton-enquete-action-secondary {
|
||||||
|
cursor: pointer;
|
||||||
|
background: sienna;
|
||||||
|
padding: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boutton-enquete-action-teal {
|
||||||
|
cursor: pointer;
|
||||||
|
background: darkblue;
|
||||||
|
padding: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-legend {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
z-index: 999999;
|
||||||
|
margin-top: 17%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-legend ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dl, ol, ul {
|
||||||
|
font-size: 11px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-parcelle-non-enquetee {
|
||||||
|
background-color: rgb(128,128,128);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-parcelle-enquetee-non-bati {
|
||||||
|
background-color: cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-parcelle-enquetee-bati {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
75
src/app/office/cartographie/cartographie.component.html
Normal file
75
src/app/office/cartographie/cartographie.component.html
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<div class="row" style="margin-top: 75px;">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<ul nz-menu nzTheme="dark" nzMode="horizontal" class="navBar">
|
||||||
|
<li nz-menu-item style="margin-left: 6% !important;" class="text-center" (click)="goHome()">
|
||||||
|
<img src="assets/sigibe/home.svg" alt="" class="icon-home-header">
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item nzSelected routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/cartographie/data/sommaire-cartographie"> Sommaire </a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li nz-menu-item nz-popover [(nzPopoverVisible)]="isVisibleReference"
|
||||||
|
(nzPopoverVisibleChange)="change($event, 1)" nzPopoverPlacement="bottom"
|
||||||
|
[nzPopoverContent]="contentTemplateMenuReference">
|
||||||
|
Références <svg data-icon="chevron-down" height="13" role="img" viewBox="0 0 16 16" width="13">
|
||||||
|
<path
|
||||||
|
d="M12 5c-.28 0-.53.11-.71.29L8 8.59l-3.29-3.3a1.003 1.003 0 00-1.42 1.42l4 4c.18.18.43.29.71.29s.53-.11.71-.29l4-4A1.003 1.003 0 0012 5z"
|
||||||
|
fill-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li nz-menu-item nzSelected routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/cartographie/data/fiche-chargement-geojson"> Fiche de chargement des fichiers GeoJSON </a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li nz-menu-item routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/cartographie/cartographie-fiscale-tfu"> Catographie sémantique des immeubles et taxes </a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #contentTemplateMenuReference>
|
||||||
|
<nz-list>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/deparrtement"> <i class="mdi mdi-arrow-right"> </i> Les départements
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/commune"> <i class="mdi mdi-arrow-right"> </i> Les communes
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/arrondissement"> <i class="mdi mdi-arrow-right"> </i> Les arrondissements
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/quartier"> <i class="mdi mdi-arrow-right"> </i> Les quartiers
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/exercice"> <i class="mdi mdi-arrow-right"> </i> Les exercices
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/zone-rfu"> <i class="mdi mdi-arrow-right"> </i> Les zones RFU
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/categorie-batiment"> <i class="mdi mdi-arrow-right"> </i> Les
|
||||||
|
catégories de bâtiment
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
<nz-list-item class="review-list-item-menu">
|
||||||
|
<a routerLink="/core/cartographie/data/reference/usage"> <i class="mdi mdi-arrow-right"> </i>
|
||||||
|
Les usages des immeubles
|
||||||
|
</a>
|
||||||
|
</nz-list-item>
|
||||||
|
|
||||||
|
</nz-list>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
56
src/app/office/cartographie/cartographie.component.ts
Normal file
56
src/app/office/cartographie/cartographie.component.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
/* fin déclaration pour outils de mesure */
|
||||||
|
@Component({
|
||||||
|
selector: 'app-cartographie',
|
||||||
|
templateUrl: './cartographie.component.html',
|
||||||
|
styleUrls: [
|
||||||
|
'./cartographie.component.css'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CartographieComponent {
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
isVisibleReference = false;
|
||||||
|
isVisibleSecteur = false;
|
||||||
|
isVisibleEquipe = false;
|
||||||
|
menuNum = 0;
|
||||||
|
|
||||||
|
module: any = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.module = JSON.parse(this.tokenStorage.getModule());
|
||||||
|
console.log(JSON.parse(this.tokenStorage.getModule()));
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
change(value: any, menuNum: number): void {
|
||||||
|
if (menuNum == 1)
|
||||||
|
this.isVisibleReference = value;
|
||||||
|
if (menuNum == 2)
|
||||||
|
this.isVisibleSecteur = value;
|
||||||
|
if (menuNum == 3)
|
||||||
|
this.isVisibleEquipe = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
goHome(): void {
|
||||||
|
this.tokenStorage.saveModule(null);
|
||||||
|
this.router.navigate(['/principale']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/app/office/cartographie/cartographie.module.ts
Normal file
44
src/app/office/cartographie/cartographie.module.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { CartographieRoutingModule } from './cartographie-routing.module';
|
||||||
|
import { CartographieComponent } from './cartographie.component';
|
||||||
|
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { ChargerFichierComponent } from './charger-fichier/charger-fichier.component';
|
||||||
|
import { DetailByNupGeomComponent } from './detail-by-nup-geom/detail-by-nup-geom.component';
|
||||||
|
import { DataTablesModule } from 'angular-datatables';
|
||||||
|
import { SharedModule } from 'src/app/shared/shared.module';
|
||||||
|
import { SommaireCartographieComponent } from './sommaire-cartographie/sommaire-cartographie.component';
|
||||||
|
import { CartographieFiscaleTfuComponent } from './cartographie-fiscale-tfu/cartographie-fiscale-tfu.component';
|
||||||
|
import { CartographieRouterComponent } from './cartographie-router/cartographie-router.component';
|
||||||
|
import { ReferenceModule } from '../reference/reference.module';
|
||||||
|
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CartographieComponent,
|
||||||
|
ChargerFichierComponent,
|
||||||
|
DetailByNupGeomComponent,
|
||||||
|
SommaireCartographieComponent,
|
||||||
|
CartographieFiscaleTfuComponent,
|
||||||
|
CartographieRouterComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
CartographieRoutingModule,
|
||||||
|
|
||||||
|
DataTablesModule,
|
||||||
|
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
SharedModule,
|
||||||
|
ReferenceModule,
|
||||||
|
NgApexchartsModule,
|
||||||
|
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
})
|
||||||
|
export class CartographieModule { }
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<h6 class="card-title" style="font-size: 16px!important;text-transform: none;"> Fiche chargement de
|
||||||
|
fichiers GeoJSON </h6>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<form [formGroup]="uploadForm" *ngIf="uploadForm">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<input class="did-floating-input" type="text" id="reference" formControlName="reference"
|
||||||
|
placeholder="Entrez la référence">
|
||||||
|
<label class="did-floating-label"> Référence du chargement <span class="text-danger">
|
||||||
|
*</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<input class="did-floating-input" style="padding-top: 10px;"
|
||||||
|
(change)="handleFileInput($event)" type="file" accept=".geojson"
|
||||||
|
placeholder="Entrez la référence" #fileInput>
|
||||||
|
<label class="did-floating-label"> Fichier à charger <span class="text-danger"> *</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<input class="did-floating-input" type="text" id="description"
|
||||||
|
formControlName="description" placeholder="Entrez la description">
|
||||||
|
<label class="did-floating-label"> Entrez une description <span class="text-danger">
|
||||||
|
*</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<button class="btn btn-review btn-secondary btn-fw mr-2" (click)="resetForm()"
|
||||||
|
[disabled]="isActionInProgress">
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-review btn-primary btn-fw" (click)="saveForm()"
|
||||||
|
[disabled]="isActionInProgress">
|
||||||
|
{{ isActionInProgress == false ? 'Enregistrer' : 'Opération en cours ...' }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 16px!important;margin-bottom: 0px;text-transform: unset;">
|
||||||
|
Liste des fichiers GeoJSON chargés </h4>
|
||||||
|
<p class="card-description text-gray" style="margin-bottom: 2%;line-height: 30px;font-size: 12px;">
|
||||||
|
Liste des différents fichiers GeoJSON chargés pour l'affichage cartographique
|
||||||
|
</p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped" datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Référence
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of fichierList; let i=index" class="border-bottom-light">
|
||||||
|
<td>
|
||||||
|
{{ todo.reference }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ todo.description }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="btn btn-icons btn-rounded btn-success mr-1">
|
||||||
|
<i class="mdi mdi-pencil btn-icon-modify"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-icons btn-rounded btn-danger">
|
||||||
|
<i class="mdi mdi-delete btn-icon-modify"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { DataTableDirective } from 'angular-datatables';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-charger-fichier',
|
||||||
|
templateUrl: './charger-fichier.component.html',
|
||||||
|
styleUrls: ['./charger-fichier.component.css']
|
||||||
|
})
|
||||||
|
export class ChargerFichierComponent implements OnInit {
|
||||||
|
|
||||||
|
fileToUpload: File | null = null;
|
||||||
|
uploadForm?: FormGroup;
|
||||||
|
@ViewChild('fileInput', { static: false }) fileInput!: ElementRef;
|
||||||
|
|
||||||
|
isActionInProgress: boolean = false;
|
||||||
|
|
||||||
|
@ViewChild(DataTableDirective, { static: false })
|
||||||
|
dtElement?: DataTableDirective;
|
||||||
|
dtOptions: DataTables.Settings = {};
|
||||||
|
dtTrigger: Subject<any> = new Subject<any>();
|
||||||
|
|
||||||
|
isForm = false;
|
||||||
|
fichierList: any[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
this.dtOptions = {
|
||||||
|
pagingType: 'full_numbers',
|
||||||
|
pageLength: 10,
|
||||||
|
processing: true,
|
||||||
|
language: {
|
||||||
|
search: "Rechercher :",
|
||||||
|
emptyTable: "Aucune donnée disponible",
|
||||||
|
lengthMenu: "Afficher _MENU_ éléments",
|
||||||
|
info: "Affichage de l'élement _START_ à _END_ sur _TOTAL_ éléments",
|
||||||
|
infoEmpty: "Affichage de l'élement 0 à 0 sur 0 éléments",
|
||||||
|
infoFiltered: "(filtré de _MAX_ éléments au total)",
|
||||||
|
paginate: {
|
||||||
|
first: "<i class='menu-icon mdi mdi-chevron-double-left'></i>",
|
||||||
|
previous: "<i class='menu-icon mdi mdi-chevron-left'></i>",
|
||||||
|
next: "<i class='menu-icon mdi mdi-chevron-right'></i>",
|
||||||
|
last: "<i class='menu-icon mdi mdi-chevron-double-right'></i>"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.makeForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
makeForm(): void {
|
||||||
|
this.uploadForm = this.fb.group({
|
||||||
|
id: [null],
|
||||||
|
reference: [null, [Validators.required]],
|
||||||
|
description: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): void {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.fichierList = [];
|
||||||
|
this.crudService.getAll('parcelle-geom/geojsonfile-all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.fichierList = data != null ? data.object : [];
|
||||||
|
this.fichierList = [...this.fichierList];
|
||||||
|
this.refreshDataTable();
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
console.log('OK');
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
refreshDataTable(): void {
|
||||||
|
this.dtElement?.dtInstance.then((dtInstance: DataTables.Api) => {
|
||||||
|
// Destroy the table first
|
||||||
|
dtInstance.destroy();
|
||||||
|
// Call the dtTrigger to rerender again
|
||||||
|
this.dtTrigger.next(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.dtTrigger.next(this.dtOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.dtTrigger.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveForm(): void {
|
||||||
|
for (const i in this.uploadForm?.controls) {
|
||||||
|
this.uploadForm?.controls[i].markAsDirty();
|
||||||
|
this.uploadForm?.controls[i].updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.uploadForm?.valid && this.fileToUpload != null) {
|
||||||
|
this.isActionInProgress = true;
|
||||||
|
const formData = this.uploadForm?.value;
|
||||||
|
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
this.crudService.saveFile(formData, this.fileToUpload).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
if (data.success == true) {
|
||||||
|
this.message.create('success', `Chargement effectué avec succès.`);
|
||||||
|
|
||||||
|
this.modal.success({
|
||||||
|
nzTitle: 'Succèss',
|
||||||
|
nzContent: data.message,
|
||||||
|
nzOnOk: () => {
|
||||||
|
this.resetForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
this.message.create('error', `Chargement du fichier erroné`);
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
this.message.create('error', `Erreur de connexion internet ou du système`);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: 'Formulaire invalid. Un ou plusieurs champs sont vides...'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetForm(): void {
|
||||||
|
this.fileInput.nativeElement.value = '';
|
||||||
|
this.uploadForm?.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleFileInput(event: any) {
|
||||||
|
//1. limiter la taille à 2Mo et contrôler l'extension si c'est du geojson
|
||||||
|
|
||||||
|
const fileSizeMB = event.target.files[0]?.size / (1024 * 1024);
|
||||||
|
if (fileSizeMB > 2) {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: 'Fichier trop volumineux. La taille maximale autorisée est de 2 Mo.',
|
||||||
|
nzOnOk: () => {
|
||||||
|
this.fileToUpload = null;
|
||||||
|
this.fileInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Vérification du type de fichier (extension/MIME)
|
||||||
|
|
||||||
|
if (!event.target.files[0]?.type.includes('geojson') && !event.target.files[0]?.type.includes('geo+json')) {
|
||||||
|
this.modal.error({
|
||||||
|
nzTitle: 'Erreur',
|
||||||
|
nzContent: 'Format de fichier erroné. Le fichier doit être de type geojson.',
|
||||||
|
nzOnOk: () => {
|
||||||
|
this.fileToUpload = null;
|
||||||
|
this.fileInput.nativeElement.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileToUpload = event.target.files[0];
|
||||||
|
console.log(this.fileToUpload?.name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900; text-transform: unset;"> DÉTAIL DES INFORMATIONS SUR LA PARCELLE </h4>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<app-detail-enquete-information
|
||||||
|
[acteurConcernesList]="acteurConcernesList"
|
||||||
|
[enquete]="enquete"
|
||||||
|
[parcelle]="parcelle">
|
||||||
|
</app-detail-enquete-information>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-detail-by-nup-geom',
|
||||||
|
templateUrl: './detail-by-nup-geom.component.html',
|
||||||
|
styleUrls: ['./detail-by-nup-geom.component.css']
|
||||||
|
})
|
||||||
|
export class DetailByNupGeomComponent implements OnInit {
|
||||||
|
|
||||||
|
acteurConcernesList: any[] = [];
|
||||||
|
enquete: any = null;
|
||||||
|
parcelle: any = null;
|
||||||
|
|
||||||
|
isActionInProgress: boolean = false;
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
nup: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private http: HttpClient,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
|
||||||
|
this.nup = this.route.snapshot.params["nup"];
|
||||||
|
|
||||||
|
console.log('this.nup', this.nup);
|
||||||
|
|
||||||
|
if (this.nup != undefined && this.nup != '') {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
|
||||||
|
const result: any = await firstValueFrom(this.crudService.getAll('enquete/fiche/nup-provisoir/' + this.nup));
|
||||||
|
console.log('enquete ===> ', result);
|
||||||
|
|
||||||
|
if (result && result.object != null) {
|
||||||
|
this.enquete = result.object.enquete;
|
||||||
|
this.parcelle = result.object.enquete?.parcelle;
|
||||||
|
this.acteurConcernesList = result.object.acteurConcernes ? result.object.acteurConcernes : [];
|
||||||
|
if (this.acteurConcernesList && this.acteurConcernesList.length > 0) {
|
||||||
|
for (let i = 0; i < this.acteurConcernesList.length; i++) {
|
||||||
|
this.acteurConcernesList[i].active = false;
|
||||||
|
if (this.acteurConcernesList[i].pieces && this.acteurConcernesList[i].pieces.length > 0) {
|
||||||
|
for (let j = 0; j < this.acteurConcernesList[i].pieces.length; j++) {
|
||||||
|
this.acteurConcernesList[i].pieces[j].active = false;
|
||||||
|
for (let k = 0; k < this.acteurConcernesList[i].pieces[j].uploads.length; k++) {
|
||||||
|
this.acteurConcernesList[i].pieces[j].uploads[k].active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,531 @@
|
|||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
SOMMAIRE CARTOGRAPHIE — styles consolidés
|
||||||
|
ViewEncapsulation.None requis
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ── Reset nz-card body ── */
|
||||||
|
.kpi-card .ant-card-body,
|
||||||
|
.stat-banner-card .ant-card-body,
|
||||||
|
.chart-card .ant-card-body,
|
||||||
|
.stats-table-card .ant-card-body,
|
||||||
|
.alert-card .ant-card-body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
DASHBOARD CONTAINER
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 24px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
KPI CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.kpi-cards-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(26, 88, 144, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 24px;
|
||||||
|
gap: 18px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-content::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-icon {
|
||||||
|
width: 56px; height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-icon [nz-icon],
|
||||||
|
.kpi-icon span[nz-icon] {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-details { flex: 1; }
|
||||||
|
|
||||||
|
.kpi-value {
|
||||||
|
font-size: 34px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variantes couleur — barre top + icône + valeur */
|
||||||
|
.kpi-card-blue .kpi-content::before { background: linear-gradient(90deg, #1a5890, #0e3660); }
|
||||||
|
.kpi-card-blue .kpi-icon [nz-icon] { color: #1a5890; }
|
||||||
|
.kpi-card-blue .kpi-value { color: #1a5890; }
|
||||||
|
|
||||||
|
.kpi-card-green .kpi-content::before { background: linear-gradient(90deg, #10b981, #059669); }
|
||||||
|
.kpi-card-green .kpi-icon [nz-icon] { color: #10b981; }
|
||||||
|
.kpi-card-green .kpi-value { color: #10b981; }
|
||||||
|
|
||||||
|
.kpi-card-purple .kpi-content::before { background: linear-gradient(90deg, #1a5890, #6d28d9); }
|
||||||
|
.kpi-card-purple .kpi-icon [nz-icon] { color: #1a5890; }
|
||||||
|
.kpi-card-purple .kpi-value { color: #1a5890; }
|
||||||
|
|
||||||
|
.kpi-card-red .kpi-content::before { background: linear-gradient(90deg, #ef4444, #dc2626); }
|
||||||
|
.kpi-card-red .kpi-icon [nz-icon] { color: #ef4444; }
|
||||||
|
.kpi-card-red .kpi-value { color: #ef4444; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
STAT BANNER
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.stat-banner-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.10);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
transition: filter 0.2s ease;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-item:hover { filter: brightness(0.96); }
|
||||||
|
|
||||||
|
.stat-banner-green { background: linear-gradient(135deg, #f0fdf4, #dcfce7); }
|
||||||
|
.stat-banner-blue { background: linear-gradient(135deg, #e8f1fb, #cce0f5); }
|
||||||
|
.stat-banner-purple { background: linear-gradient(135deg, #eef2fb, #d6e4f5); }
|
||||||
|
.stat-banner-orange { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
|
||||||
|
|
||||||
|
.stat-banner-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
width: 48px; height: 48px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-green .stat-banner-icon { color: #10b981; background: rgba(16, 185, 129, 0.12); }
|
||||||
|
.stat-banner-blue .stat-banner-icon { color: #1a5890; background: rgba(26, 88, 144, 0.12); }
|
||||||
|
.stat-banner-purple .stat-banner-icon { color: #1a5890; background: rgba(26, 88, 144, 0.10); }
|
||||||
|
.stat-banner-orange .stat-banner-icon { color: #f59e0b; background: rgba(245, 158, 11, 0.12); }
|
||||||
|
|
||||||
|
.stat-banner-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex; flex-direction: column; gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-value {
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-unit {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar {
|
||||||
|
width: 100%; height: 5px;
|
||||||
|
background: rgba(0,0,0,0.07);
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar-fill.green { background: #10b981; }
|
||||||
|
.stat-banner-bar-fill.blue { background: #1a5890; }
|
||||||
|
.stat-banner-bar-fill.purple { background: #1a5890; }
|
||||||
|
.stat-banner-bar-fill.orange { background: #f59e0b; }
|
||||||
|
|
||||||
|
.stat-banner-percent {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #9ca3af;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-divider {
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(0,0,0,0.07);
|
||||||
|
margin: 12px 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ONGLETS THÉMATIQUES
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.thematique-tabs-section {
|
||||||
|
margin-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thematique-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
padding: 10px 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #6b7280;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab:hover {
|
||||||
|
color: #1a5890;
|
||||||
|
background: #e8f1fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab-active {
|
||||||
|
color: #1a5890 !important;
|
||||||
|
border-bottom-color: #1a5890 !important;
|
||||||
|
background: #e8f1fb !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thematique-content {
|
||||||
|
animation: fadeInUp 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
CHART CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.chart-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.08);
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a5890;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title [nz-icon] {
|
||||||
|
color: #1a5890;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-content {
|
||||||
|
padding: 16px 20px;
|
||||||
|
min-height: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: #f4f8fd;
|
||||||
|
border-top: 1px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Legend custom ── */
|
||||||
|
.chart-legend-custom {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex; align-items: center; gap: 8px; font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
width: 11px; height: 11px;
|
||||||
|
border-radius: 50%; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-text { flex: 1; color: #6b7280; font-weight: 500; }
|
||||||
|
.legend-value { color: #1a5890; font-weight: 700; }
|
||||||
|
|
||||||
|
/* ── Summary ── */
|
||||||
|
.chart-summary {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
font-size: 11px; color: #6b7280; font-weight: 500;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
font-size: 22px; font-weight: 700; color: #1a5890;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value.active { color: #10b981; }
|
||||||
|
.summary-value.inactive { color: #ef4444; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
LÉGENDE STATUTS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.statut-legend-list {
|
||||||
|
padding: 14px 18px;
|
||||||
|
display: flex; flex-direction: column; gap: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-item {
|
||||||
|
display: flex; align-items: flex-start; gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-dot {
|
||||||
|
width: 13px; height: 13px;
|
||||||
|
border-radius: 50%; flex-shrink: 0; margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-body {
|
||||||
|
flex: 1; display: flex; flex-direction: column; gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-label {
|
||||||
|
font-size: 12px; font-weight: 600; color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-bar-wrap {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-bar {
|
||||||
|
flex: 1; height: 5px;
|
||||||
|
background: #e0ecf8; border-radius: 999px; overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-bar-fill {
|
||||||
|
height: 100%; border-radius: 999px;
|
||||||
|
transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-val {
|
||||||
|
font-size: 12px; font-weight: 700; color: #1a5890; white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statut-legend-val small {
|
||||||
|
font-size: 11px; color: #9ca3af; font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
STATS TABLE CARD
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.stats-table-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
font-size: 14px; font-weight: 700; color: #1a5890;
|
||||||
|
margin: 0; display: flex; align-items: center; gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title [nz-icon] { color: #1a5890; font-size: 18px; }
|
||||||
|
|
||||||
|
.fonction-name { font-weight: 600; color: #1a5890; }
|
||||||
|
|
||||||
|
.ant-table { font-size: 13px; }
|
||||||
|
|
||||||
|
.ant-table thead > tr > th {
|
||||||
|
background: #e8f1fb !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: #1a5890 !important;
|
||||||
|
border-bottom: 2px solid #c5d9ef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table tbody > tr:hover > td { background: #f4f8fd !important; }
|
||||||
|
.ant-table tbody > tr > td { padding: 14px 16px !important; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ALERT CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.alert-card {
|
||||||
|
border-radius: 12px; border: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
display: flex; align-items: flex-start;
|
||||||
|
gap: 16px; padding: 20px; border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-icon { font-size: 30px; flex-shrink: 0; margin-top: 2px; }
|
||||||
|
.alert-body { flex: 1; }
|
||||||
|
|
||||||
|
.alert-title {
|
||||||
|
font-size: 11px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-value {
|
||||||
|
font-size: 26px; font-weight: 700; line-height: 1.1; margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-desc { font-size: 12px; opacity: 0.72; }
|
||||||
|
|
||||||
|
/* Warning */
|
||||||
|
.alert-warning { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
|
||||||
|
.alert-warning .alert-icon { color: #f59e0b; }
|
||||||
|
.alert-warning .alert-title { color: #92400e; }
|
||||||
|
.alert-warning .alert-value { color: #d97706; }
|
||||||
|
.alert-warning .alert-desc { color: #78350f; }
|
||||||
|
|
||||||
|
/* Danger */
|
||||||
|
.alert-danger { background: linear-gradient(135deg, #fff1f2, #ffe4e6); }
|
||||||
|
.alert-danger .alert-icon { color: #ef4444; }
|
||||||
|
.alert-danger .alert-title { color: #991b1b; }
|
||||||
|
.alert-danger .alert-value { color: #dc2626; }
|
||||||
|
.alert-danger .alert-desc { color: #7f1d1d; }
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
.alert-success { background: linear-gradient(135deg, #f0fdf4, #dcfce7); }
|
||||||
|
.alert-success .alert-icon { color: #10b981; }
|
||||||
|
.alert-success .alert-title { color: #14532d; }
|
||||||
|
.alert-success .alert-value { color: #16a34a; }
|
||||||
|
.alert-success .alert-desc { color: #15803d; }
|
||||||
|
|
||||||
|
/* Info — teinte bleue */
|
||||||
|
.alert-info { background: linear-gradient(135deg, #e8f1fb, #cce0f5); }
|
||||||
|
.alert-info .alert-icon { color: #1a5890; }
|
||||||
|
.alert-info .alert-title { color: #0e3660; }
|
||||||
|
.alert-info .alert-value { color: #1a5890; }
|
||||||
|
.alert-info .alert-desc { color: #2563eb; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
DIVERS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.statut-dot-table {
|
||||||
|
display: inline-block;
|
||||||
|
width: 14px; height: 14px; border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ANIMATIONS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from { opacity: 0; transform: translateY(16px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card { animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
.chart-card { animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
.stats-table-card{ animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
|
||||||
|
.kpi-card:nth-child(1) { animation-delay: 0.05s; }
|
||||||
|
.kpi-card:nth-child(2) { animation-delay: 0.12s; }
|
||||||
|
.kpi-card:nth-child(3) { animation-delay: 0.19s; }
|
||||||
|
.kpi-card:nth-child(4) { animation-delay: 0.26s; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
RESPONSIVE
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.stat-banner-container { flex-wrap: wrap; }
|
||||||
|
.stat-banner-item { flex: 0 0 50%; min-width: 0; }
|
||||||
|
.stat-banner-divider { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dashboard-container { padding: 12px; }
|
||||||
|
.stat-banner-container { flex-direction: column; }
|
||||||
|
.stat-banner-item { flex: 1 1 100%; }
|
||||||
|
.thematique-tabs { gap: 2px; }
|
||||||
|
.ttab { padding: 8px 10px; font-size: 12px; }
|
||||||
|
.kpi-content { padding: 14px 16px; }
|
||||||
|
.kpi-value { font-size: 26px; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,489 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<div class="dashboard-container">
|
||||||
|
|
||||||
|
<!-- ── KPIs principaux ── -->
|
||||||
|
<div class="kpi-cards-section">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-blue" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="global" nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">{{ statistiquesGlobales.totalParcelles | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="kpi-label">Total Parcelles</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-green" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="check-circle" nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">{{ statistiquesGlobales.parcellesEnquetees | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="kpi-label">Parcelles Enquêtées</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-purple" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="environment" nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">{{ statistiquesGlobales.parcellesGeoreferencees | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="kpi-label">Géoréférencées</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-red" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="warning" nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">{{ statistiquesGlobales.parcellesAvecDonneesNonGeo | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="kpi-label">Données sans géom.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Bandeaux secondaires ── -->
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stat-banner-card" [nzLoading]="loading">
|
||||||
|
<div class="stat-banner-container">
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-green">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="check-circle" nzTheme="fill"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getTauxEnquete() }}<span class="stat-banner-unit">%</span></div>
|
||||||
|
<div class="stat-banner-label">Taux d'enquête</div>
|
||||||
|
<div class="stat-banner-bar"><div class="stat-banner-bar-fill green" [style.width]="getTauxEnquete() + '%'"></div></div>
|
||||||
|
<div class="stat-banner-percent">{{ statistiquesGlobales.parcellesEnquetees }} / {{ statistiquesGlobales.totalParcelles }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-blue">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="home" nzTheme="fill"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getTauxBati() }}<span class="stat-banner-unit">%</span></div>
|
||||||
|
<div class="stat-banner-label">Taux de bâti</div>
|
||||||
|
<div class="stat-banner-bar"><div class="stat-banner-bar-fill blue" [style.width]="getTauxBati() + '%'"></div></div>
|
||||||
|
<div class="stat-banner-percent">{{ statistiquesGlobales.parcellesBaties }} bâties / {{ statistiquesGlobales.parcellesNonBaties }} non bâties</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-purple">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="environment" nzTheme="fill"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getTauxGeo() }}<span class="stat-banner-unit">%</span></div>
|
||||||
|
<div class="stat-banner-label">Taux de géoréférencement</div>
|
||||||
|
<div class="stat-banner-bar"><div class="stat-banner-bar-fill purple" [style.width]="getTauxGeo() + '%'"></div></div>
|
||||||
|
<div class="stat-banner-percent">{{ statistiquesGlobales.parcellesGeoreferencees }} géoréf. / {{ statistiquesGlobales.parcellesNonGeoreferencees }} sans géom.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-orange">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="rise" nzTheme="outline"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getTauxAJour() }}<span class="stat-banner-unit">%</span></div>
|
||||||
|
<div class="stat-banner-label">Taux à jour fiscal</div>
|
||||||
|
<div class="stat-banner-bar"><div class="stat-banner-bar-fill orange" [style.width]="getTauxAJour() + '%'"></div></div>
|
||||||
|
<div class="stat-banner-percent">{{ statistiquesGlobales.parcellesEndettees }} endettées</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Onglets thématiques ── -->
|
||||||
|
<div class="thematique-tabs-section">
|
||||||
|
<div class="thematique-tabs">
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'statuts'" (click)="activeThematique = 'statuts'">
|
||||||
|
<span nz-icon nzType="pie-chart" nzTheme="outline"></span> Statuts des parcelles
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'enquete'" (click)="activeThematique = 'enquete'">
|
||||||
|
<span nz-icon nzType="audit" nzTheme="outline"></span> Enquête par territoire
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'georef'" (click)="activeThematique = 'georef'">
|
||||||
|
<span nz-icon nzType="environment" nzTheme="outline"></span> Géoréférencement
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'fiscal'" (click)="activeThematique = 'fiscal'">
|
||||||
|
<span nz-icon nzType="dollar-circle" nzTheme="outline"></span> Situation fiscale
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── THÉMATIQUE : Statuts ── -->
|
||||||
|
<div *ngIf="activeThematique === 'statuts'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Légende statuts -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="unordered-list"></span> Légende des statuts</h3>
|
||||||
|
</div>
|
||||||
|
<div class="statut-legend-list">
|
||||||
|
<div class="statut-legend-item" *ngFor="let s of statsParStatut">
|
||||||
|
<div class="statut-legend-dot" [style.background]="s.couleur"></div>
|
||||||
|
<div class="statut-legend-body">
|
||||||
|
<span class="statut-legend-label">{{ s.libelle }}</span>
|
||||||
|
<div class="statut-legend-bar-wrap">
|
||||||
|
<div class="statut-legend-bar">
|
||||||
|
<div class="statut-legend-bar-fill" [style.width]="s.pourcentage + '%'" [style.background]="s.couleur"></div>
|
||||||
|
</div>
|
||||||
|
<span class="statut-legend-val">{{ s.nombre | number:'1.0-0': 'fr' }} <small>({{ s.pourcentage }}%)</small></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Donut statuts -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="pie-chart"></span> Répartition par statut</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="pieStatutsOptions.series"
|
||||||
|
[chart]="pieStatutsOptions.chart"
|
||||||
|
[labels]="pieStatutsOptions.labels"
|
||||||
|
[colors]="pieStatutsOptions.colors"
|
||||||
|
[legend]="pieStatutsOptions.legend"
|
||||||
|
[plotOptions]="pieStatutsOptions.plotOptions"
|
||||||
|
[dataLabels]="pieStatutsOptions.dataLabels"
|
||||||
|
[responsive]="pieStatutsOptions.responsive">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pie bâties -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="home"></span> Bâties vs Non bâties</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="pieBatieOptions.series"
|
||||||
|
[chart]="pieBatieOptions.chart"
|
||||||
|
[labels]="pieBatieOptions.labels"
|
||||||
|
[colors]="pieBatieOptions.colors"
|
||||||
|
[legend]="pieBatieOptions.legend"
|
||||||
|
[plotOptions]="pieBatieOptions.plotOptions"
|
||||||
|
[dataLabels]="pieBatieOptions.dataLabels"
|
||||||
|
[responsive]="pieBatieOptions.responsive">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="chart-footer">
|
||||||
|
<div class="chart-summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">Bâties :</span>
|
||||||
|
<span class="summary-value active">{{ statistiquesGlobales.parcellesBaties | number:'1.0-0': 'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">Non bâties :</span>
|
||||||
|
<span class="summary-value inactive">{{ statistiquesGlobales.parcellesNonBaties | number:'1.0-0': 'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── THÉMATIQUE : Enquête par territoire ── -->
|
||||||
|
<div *ngIf="activeThematique === 'enquete'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Bar par commune -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bar-chart"></span> Enquête par commune</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="barCommuneOptions.series"
|
||||||
|
[chart]="barCommuneOptions.chart"
|
||||||
|
[xaxis]="barCommuneOptions.xaxis"
|
||||||
|
[yaxis]="barCommuneOptions.yaxis"
|
||||||
|
[colors]="barCommuneOptions.colors"
|
||||||
|
[legend]="barCommuneOptions.legend"
|
||||||
|
[stroke]="barCommuneOptions.stroke"
|
||||||
|
[markers]="barCommuneOptions.markers"
|
||||||
|
[grid]="barCommuneOptions.grid"
|
||||||
|
[dataLabels]="barCommuneOptions.dataLabels"
|
||||||
|
[plotOptions]="barCommuneOptions.plotOptions"
|
||||||
|
[tooltip]="barCommuneOptions.tooltip"
|
||||||
|
[fill]="barCommuneOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bar par structure -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bank"></span> Enquête par structure</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="barStructureOptions.series"
|
||||||
|
[chart]="barStructureOptions.chart"
|
||||||
|
[xaxis]="barStructureOptions.xaxis"
|
||||||
|
[yaxis]="barStructureOptions.yaxis"
|
||||||
|
[colors]="barStructureOptions.colors"
|
||||||
|
[legend]="barStructureOptions.legend"
|
||||||
|
[stroke]="barStructureOptions.stroke"
|
||||||
|
[markers]="barStructureOptions.markers"
|
||||||
|
[grid]="barStructureOptions.grid"
|
||||||
|
[dataLabels]="barStructureOptions.dataLabels"
|
||||||
|
[plotOptions]="barStructureOptions.plotOptions"
|
||||||
|
[tooltip]="barStructureOptions.tooltip"
|
||||||
|
[fill]="barStructureOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tableau par commune -->
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stats-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail par commune</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #communeTable [nzData]="statsParCommune" [nzPageSize]="5">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Commune</th>
|
||||||
|
<th nzAlign="center">Total</th>
|
||||||
|
<th nzAlign="center">Enquêtées</th>
|
||||||
|
<th nzAlign="center">Non enquêtées</th>
|
||||||
|
<th nzAlign="center">Taux d'enquête</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of communeTable.data">
|
||||||
|
<td><span class="fonction-name">{{ item.commune }}</span></td>
|
||||||
|
<td nzAlign="center"><nz-tag [nzColor]="'blue'">{{ item.total | number:'1.0-0': 'fr' }}</nz-tag></td>
|
||||||
|
<td nzAlign="center"><nz-tag [nzColor]="'green'">{{ item.enquetees | number:'1.0-0': 'fr' }}</nz-tag></td>
|
||||||
|
<td nzAlign="center"><nz-tag [nzColor]="'default'">{{ item.nonEnquetees | number:'1.0-0': 'fr' }}</nz-tag></td>
|
||||||
|
<td nzAlign="center">
|
||||||
|
<nz-progress [nzPercent]="item.tauxEnquete"
|
||||||
|
[nzStrokeColor]="getProgressColor(item.tauxEnquete)"
|
||||||
|
[nzShowInfo]="true" nzSize="small">
|
||||||
|
</nz-progress>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── THÉMATIQUE : Géoréférencement ── -->
|
||||||
|
<div *ngIf="activeThematique === 'georef'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="pie-chart"></span> Géoréférencement global</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="pieGeoOptions.series"
|
||||||
|
[chart]="pieGeoOptions.chart"
|
||||||
|
[labels]="pieGeoOptions.labels"
|
||||||
|
[colors]="pieGeoOptions.colors"
|
||||||
|
[legend]="pieGeoOptions.legend"
|
||||||
|
[plotOptions]="pieGeoOptions.plotOptions"
|
||||||
|
[dataLabels]="pieGeoOptions.dataLabels"
|
||||||
|
[responsive]="pieGeoOptions.responsive">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="chart-footer">
|
||||||
|
<div class="chart-summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">Géoréférencées :</span>
|
||||||
|
<span class="summary-value active">{{ statistiquesGlobales.parcellesGeoreferencees | number:'1.0-0': 'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span class="summary-label">Sans géométrie :</span>
|
||||||
|
<span class="summary-value inactive">{{ statistiquesGlobales.parcellesNonGeoreferencees | number:'1.0-0': 'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-7">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bar-chart"></span> Géoréférencement par commune</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart
|
||||||
|
[series]="barGeoParCommuneOptions.series"
|
||||||
|
[chart]="barGeoParCommuneOptions.chart"
|
||||||
|
[xaxis]="barGeoParCommuneOptions.xaxis"
|
||||||
|
[yaxis]="barGeoParCommuneOptions.yaxis"
|
||||||
|
[colors]="barGeoParCommuneOptions.colors"
|
||||||
|
[legend]="barGeoParCommuneOptions.legend"
|
||||||
|
[stroke]="barGeoParCommuneOptions.stroke"
|
||||||
|
[markers]="barGeoParCommuneOptions.markers"
|
||||||
|
[grid]="barGeoParCommuneOptions.grid"
|
||||||
|
[dataLabels]="barGeoParCommuneOptions.dataLabels"
|
||||||
|
[plotOptions]="barGeoParCommuneOptions.plotOptions"
|
||||||
|
[tooltip]="barGeoParCommuneOptions.tooltip"
|
||||||
|
[fill]="barGeoParCommuneOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-card class="alert-card m-3">
|
||||||
|
<div class="alert-content alert-warning">
|
||||||
|
<span nz-icon nzType="warning" nzTheme="fill" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Données attributaires sans géométrie</div>
|
||||||
|
<div class="alert-value">{{ statistiquesGlobales.parcellesAvecDonneesNonGeo | number:'1.0-0': 'fr' }} parcelles</div>
|
||||||
|
<div class="alert-desc">Ces parcelles ont des données fiscales mais ne sont pas encore géoréférencées.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
|
||||||
|
</nz-card>
|
||||||
|
|
||||||
|
<!-- Alerte données sans géom -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── THÉMATIQUE : Situation fiscale ── -->
|
||||||
|
<div *ngIf="activeThematique === 'fiscal'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-danger">
|
||||||
|
<span nz-icon nzType="close-circle" nzTheme="fill" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Parcelles endettées</div>
|
||||||
|
<div class="alert-value">{{ statistiquesGlobales.parcellesEndettees | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="alert-desc">Parcelles avec arriérés fiscaux non réglés.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-success">
|
||||||
|
<span nz-icon nzType="check-circle" nzTheme="fill" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Parcelles à jour</div>
|
||||||
|
<div class="alert-value">{{ statistiquesGlobales.parcellesAJour | number:'1.0-0': 'fr' }}</div>
|
||||||
|
<div class="alert-desc">Parcelles dont la situation fiscale est régularisée.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-info">
|
||||||
|
<span nz-icon nzType="rise" nzTheme="outline" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Taux de régularisation</div>
|
||||||
|
<div class="alert-value">{{ getTauxAJour() }}%</div>
|
||||||
|
<div class="alert-desc">Part des parcelles à jour sur le total enquêté.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tableau statuts fiscaux -->
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stats-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail par statut fiscal</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #statutTable [nzData]="statsParStatut" [nzPageSize]="10" [nzShowPagination]="false">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Couleur</th>
|
||||||
|
<th>Statut</th>
|
||||||
|
<th nzAlign="center">Nombre</th>
|
||||||
|
<th nzAlign="center">Pourcentage</th>
|
||||||
|
<th>Progression</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of statutTable.data">
|
||||||
|
<td>
|
||||||
|
<span class="statut-dot-table" [style.background]="item.couleur"></span>
|
||||||
|
</td>
|
||||||
|
<td><span class="fonction-name">{{ item.libelle }}</span></td>
|
||||||
|
<td nzAlign="center"><nz-tag [nzColor]="'default'">{{ item.nombre | number:'1.0-0': 'fr' }}</nz-tag></td>
|
||||||
|
<td nzAlign="center"><strong>{{ item.pourcentage }}%</strong></td>
|
||||||
|
<td>
|
||||||
|
<nz-progress [nzPercent]="item.pourcentage"
|
||||||
|
[nzStrokeColor]="item.couleur"
|
||||||
|
[nzShowInfo]="false" nzSize="small">
|
||||||
|
</nz-progress>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin thematique-tabs-section -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,388 @@
|
|||||||
|
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import {
|
||||||
|
ChartComponent,
|
||||||
|
ApexChart,
|
||||||
|
ApexAxisChartSeries,
|
||||||
|
ApexXAxis,
|
||||||
|
ApexYAxis,
|
||||||
|
ApexLegend,
|
||||||
|
ApexStroke,
|
||||||
|
ApexMarkers,
|
||||||
|
ApexGrid,
|
||||||
|
ApexDataLabels,
|
||||||
|
ApexTooltip,
|
||||||
|
ApexPlotOptions,
|
||||||
|
ApexNonAxisChartSeries,
|
||||||
|
ApexResponsive,
|
||||||
|
ApexFill
|
||||||
|
} from 'ng-apexcharts';
|
||||||
|
|
||||||
|
export interface StatutParcelle {
|
||||||
|
couleur: string;
|
||||||
|
code: string;
|
||||||
|
libelle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatistiquesGlobales {
|
||||||
|
totalParcelles: number;
|
||||||
|
parcellesBaties: number;
|
||||||
|
parcellesNonBaties: number;
|
||||||
|
parcellesEnquetees: number;
|
||||||
|
parcellesNonEnquetees: number;
|
||||||
|
parcellesGeoreferencees: number;
|
||||||
|
parcellesNonGeoreferencees: number;
|
||||||
|
parcellesAJour: number;
|
||||||
|
parcellesEndettees: number;
|
||||||
|
parcellesAvecDonneesNonGeo: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParStatut {
|
||||||
|
code: string;
|
||||||
|
libelle: string;
|
||||||
|
couleur: string;
|
||||||
|
nombre: number;
|
||||||
|
pourcentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParCommune {
|
||||||
|
commune: string;
|
||||||
|
enquetees: number;
|
||||||
|
nonEnquetees: number;
|
||||||
|
total: number;
|
||||||
|
tauxEnquete: number;
|
||||||
|
georeferencees: number;
|
||||||
|
nonGeoreferencees: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParStructure {
|
||||||
|
structure: string;
|
||||||
|
enquetees: number;
|
||||||
|
nonEnquetees: number;
|
||||||
|
total: number;
|
||||||
|
tauxEnquete: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PieChartOptions = {
|
||||||
|
series: ApexNonAxisChartSeries;
|
||||||
|
chart: ApexChart;
|
||||||
|
labels: string[];
|
||||||
|
colors: string[];
|
||||||
|
legend: ApexLegend;
|
||||||
|
plotOptions: ApexPlotOptions;
|
||||||
|
dataLabels: ApexDataLabels;
|
||||||
|
responsive: ApexResponsive[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BarChartOptions = {
|
||||||
|
series: ApexAxisChartSeries;
|
||||||
|
chart: ApexChart;
|
||||||
|
xaxis: ApexXAxis;
|
||||||
|
yaxis: ApexYAxis;
|
||||||
|
colors: string[];
|
||||||
|
legend: ApexLegend;
|
||||||
|
stroke: ApexStroke;
|
||||||
|
markers: ApexMarkers;
|
||||||
|
grid: ApexGrid;
|
||||||
|
dataLabels: ApexDataLabels;
|
||||||
|
tooltip: ApexTooltip;
|
||||||
|
plotOptions: ApexPlotOptions;
|
||||||
|
fill: ApexFill;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sommaire-cartographie',
|
||||||
|
templateUrl: './sommaire-cartographie.component.html',
|
||||||
|
styleUrls: ['./sommaire-cartographie.component.css'],
|
||||||
|
encapsulation: ViewEncapsulation.None // ← ajouter ceci
|
||||||
|
})
|
||||||
|
export class SommaireCartographieComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild('chart') chart!: ChartComponent;
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
activeThematique = 'statuts';
|
||||||
|
|
||||||
|
readonly statutsParcelle: StatutParcelle[] = [
|
||||||
|
{ couleur: '#94a3b8', code: 'NON_ENQUETER', libelle: 'Parcelles non enquêtées' },
|
||||||
|
{ couleur: '#06b6d4', code: 'ENQUETER_NON_BATIE_AJOUR', libelle: 'Non bâties — À jour' },
|
||||||
|
{ couleur: '#22c55e', code: 'ENQUETER_BATIE_AJOUR', libelle: 'Bâties — À jour' },
|
||||||
|
{ couleur: '#f59e0b', code: 'ENQUETER_NON_BATIE_NON_AJOUR', libelle: 'Non bâties — Non à jour' },
|
||||||
|
{ couleur: '#f97316', code: 'ENQUETER_BATIE_NON_AJOUR', libelle: 'Bâties — Non à jour' },
|
||||||
|
{ couleur: '#ef4444', code: 'PARCELLE_ENDETTE', libelle: 'Parcelles endettées' },
|
||||||
|
{ couleur: '#8b5cf6', code: 'PARCELLE_A_JOUR_DU_FISC', libelle: 'Parcelles à jour du fisc' },
|
||||||
|
];
|
||||||
|
|
||||||
|
statistiquesGlobales: StatistiquesGlobales = {
|
||||||
|
totalParcelles: 0,
|
||||||
|
parcellesBaties: 0,
|
||||||
|
parcellesNonBaties: 0,
|
||||||
|
parcellesEnquetees: 0,
|
||||||
|
parcellesNonEnquetees: 0,
|
||||||
|
parcellesGeoreferencees: 0,
|
||||||
|
parcellesNonGeoreferencees: 0,
|
||||||
|
parcellesAJour: 0,
|
||||||
|
parcellesEndettees: 0,
|
||||||
|
parcellesAvecDonneesNonGeo: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
statsParStatut: StatParStatut[] = [];
|
||||||
|
statsParCommune: StatParCommune[] = [];
|
||||||
|
statsParStructure: StatParStructure[] = [];
|
||||||
|
|
||||||
|
// ── Charts ────────────────────────────────────────────────────────────
|
||||||
|
pieStatutsOptions: Partial<PieChartOptions> = {};
|
||||||
|
pieBatieOptions: Partial<PieChartOptions> = {};
|
||||||
|
pieGeoOptions: Partial<PieChartOptions> = {};
|
||||||
|
barCommuneOptions: Partial<BarChartOptions> = {};
|
||||||
|
barStructureOptions: Partial<BarChartOptions> = {};
|
||||||
|
barGeoParCommuneOptions: Partial<BarChartOptions> = {};
|
||||||
|
|
||||||
|
constructor(private message: NzMessageService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData(): void {
|
||||||
|
this.loading = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.statistiquesGlobales = {
|
||||||
|
totalParcelles: 4820,
|
||||||
|
parcellesBaties: 2974,
|
||||||
|
parcellesNonBaties: 1846,
|
||||||
|
parcellesEnquetees: 3640,
|
||||||
|
parcellesNonEnquetees: 1180,
|
||||||
|
parcellesGeoreferencees: 3210,
|
||||||
|
parcellesNonGeoreferencees: 1610,
|
||||||
|
parcellesAJour: 2140,
|
||||||
|
parcellesEndettees: 480,
|
||||||
|
parcellesAvecDonneesNonGeo: 890
|
||||||
|
};
|
||||||
|
|
||||||
|
this.statsParStatut = [
|
||||||
|
{ code: 'NON_ENQUETER', libelle: 'Non enquêtées', couleur: '#94a3b8', nombre: 1180, pourcentage: 24.5 },
|
||||||
|
{ code: 'ENQUETER_NON_BATIE_AJOUR', libelle: 'Non bâties — À jour', couleur: '#06b6d4', nombre: 620, pourcentage: 12.9 },
|
||||||
|
{ code: 'ENQUETER_BATIE_AJOUR', libelle: 'Bâties — À jour', couleur: '#22c55e', nombre: 1520, pourcentage: 31.5 },
|
||||||
|
{ code: 'ENQUETER_NON_BATIE_NON_AJOUR', libelle: 'Non bâties — N.à j.', couleur: '#f59e0b', nombre: 410, pourcentage: 8.5 },
|
||||||
|
{ code: 'ENQUETER_BATIE_NON_AJOUR', libelle: 'Bâties — N.à j.', couleur: '#f97316', nombre: 610, pourcentage: 12.7 },
|
||||||
|
{ code: 'PARCELLE_ENDETTE', libelle: 'Endettées', couleur: '#ef4444', nombre: 480, pourcentage: 10.0 },
|
||||||
|
{ code: 'PARCELLE_A_JOUR_DU_FISC', libelle: 'À jour du fisc', couleur: '#8b5cf6', nombre: 0, pourcentage: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsParCommune = [
|
||||||
|
{ commune: 'Cotonou', enquetees: 1240, nonEnquetees: 310, total: 1550, tauxEnquete: 80, georeferencees: 1100, nonGeoreferencees: 450 },
|
||||||
|
{ commune: 'Porto-Novo', enquetees: 860, nonEnquetees: 290, total: 1150, tauxEnquete: 75, georeferencees: 740, nonGeoreferencees: 410 },
|
||||||
|
{ commune: 'Parakou', enquetees: 620, nonEnquetees: 280, total: 900, tauxEnquete: 69, georeferencees: 510, nonGeoreferencees: 390 },
|
||||||
|
{ commune: 'Abomey-Calavi',enquetees: 510, nonEnquetees: 190, total: 700, tauxEnquete: 73, georeferencees: 430, nonGeoreferencees: 270 },
|
||||||
|
{ commune: 'Natitingou', enquetees: 410, nonEnquetees: 110, total: 520, tauxEnquete: 79, georeferencees: 430, nonGeoreferencees: 90 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsParStructure = [
|
||||||
|
{ structure: 'DGI Cotonou', enquetees: 920, nonEnquetees: 230, total: 1150, tauxEnquete: 80 },
|
||||||
|
{ structure: 'DGI Porto-Novo', enquetees: 710, nonEnquetees: 240, total: 950, tauxEnquete: 75 },
|
||||||
|
{ structure: 'Centre Impôts Sud',enquetees: 580, nonEnquetees: 220, total: 800, tauxEnquete: 73 },
|
||||||
|
{ structure: 'Centre Impôts Nord',enquetees: 430, nonEnquetees: 170, total: 600, tauxEnquete: 72 },
|
||||||
|
{ structure: 'Service Calavi', enquetees: 320, nonEnquetees: 120, total: 440, tauxEnquete: 73 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
this.buildAllCharts();
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAllCharts(): void {
|
||||||
|
this.buildPieStatuts();
|
||||||
|
this.buildPieBatie();
|
||||||
|
this.buildPieGeo();
|
||||||
|
this.buildBarCommune();
|
||||||
|
this.buildBarStructure();
|
||||||
|
this.buildBarGeoParCommune();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pie : répartition par statut ─────────────────────────────────────
|
||||||
|
buildPieStatuts(): void {
|
||||||
|
const nonZero = this.statsParStatut.filter(s => s.nombre > 0);
|
||||||
|
this.pieStatutsOptions = {
|
||||||
|
series: nonZero.map(s => s.nombre),
|
||||||
|
chart: { type: 'donut', height: 360, fontFamily: 'Inter, sans-serif',
|
||||||
|
animations: { enabled: true, easing: 'easeinout', speed: 700 } },
|
||||||
|
labels: nonZero.map(s => s.libelle),
|
||||||
|
colors: nonZero.map(s => s.couleur),
|
||||||
|
legend: { position: 'bottom', fontSize: '12px', fontWeight: 500, labels: { colors: '#1f2937' } },
|
||||||
|
plotOptions: {
|
||||||
|
pie: {
|
||||||
|
donut: {
|
||||||
|
size: '68%',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
name: { show: true, fontSize: '14px', fontWeight: 600 },
|
||||||
|
value: { show: true, fontSize: '20px', fontWeight: 700,
|
||||||
|
formatter: (val: string) => val + ' parc.' },
|
||||||
|
total: { show: true, label: 'Total', fontSize: '14px',
|
||||||
|
formatter: (w: any) => w.globals.seriesTotals.reduce((a: number, b: number) => a + b, 0) + ' parc.' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
responsive: [{ breakpoint: 480, options: { chart: { height: 280 }, legend: { position: 'bottom' } } }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pie : bâties vs non bâties ────────────────────────────────────────
|
||||||
|
buildPieBatie(): void {
|
||||||
|
this.pieBatieOptions = {
|
||||||
|
series: [this.statistiquesGlobales.parcellesBaties, this.statistiquesGlobales.parcellesNonBaties],
|
||||||
|
chart: { type: 'pie', height: 300, fontFamily: 'Inter, sans-serif',
|
||||||
|
animations: { enabled: true, easing: 'easeinout', speed: 700 } },
|
||||||
|
labels: ['Parcelles Bâties', 'Parcelles Non Bâties'],
|
||||||
|
colors: ['#10b981', '#f59e0b'],
|
||||||
|
legend: { position: 'bottom', fontSize: '12px' },
|
||||||
|
plotOptions: { pie: { expandOnClick: true } },
|
||||||
|
dataLabels: { enabled: true, formatter: (val: number) => val.toFixed(1) + '%',
|
||||||
|
style: { fontSize: '12px', fontWeight: 600, colors: ['#fff'] } },
|
||||||
|
responsive: [{ breakpoint: 480, options: { chart: { height: 250 } } }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pie : géoréférencées vs non géoréférencées ────────────────────────
|
||||||
|
buildPieGeo(): void {
|
||||||
|
this.pieGeoOptions = {
|
||||||
|
series: [
|
||||||
|
this.statistiquesGlobales.parcellesGeoreferencees,
|
||||||
|
this.statistiquesGlobales.parcellesNonGeoreferencees
|
||||||
|
],
|
||||||
|
chart: { type: 'pie', height: 300, fontFamily: 'Inter, sans-serif',
|
||||||
|
animations: { enabled: true, easing: 'easeinout', speed: 700 } },
|
||||||
|
labels: ['Géoréférencées', 'Non géoréférencées'],
|
||||||
|
colors: ['#3b82f6', '#e11d48'],
|
||||||
|
legend: { position: 'bottom', fontSize: '12px' },
|
||||||
|
plotOptions: { pie: { expandOnClick: true } },
|
||||||
|
dataLabels: { enabled: true, formatter: (val: number) => val.toFixed(1) + '%',
|
||||||
|
style: { fontSize: '12px', fontWeight: 600, colors: ['#fff'] } },
|
||||||
|
responsive: [{ breakpoint: 480, options: { chart: { height: 250 } } }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar : enquêtées vs non enquêtées par commune ──────────────────────
|
||||||
|
buildBarCommune(): void {
|
||||||
|
this.barCommuneOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Enquêtées', data: this.statsParCommune.map(c => c.enquetees) },
|
||||||
|
{ name: 'Non enquêtées', data: this.statsParCommune.map(c => c.nonEnquetees) }
|
||||||
|
],
|
||||||
|
chart: { type: 'bar', height: 340, stacked: false, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 } },
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '55%', borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParCommune.map(c => c.commune),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
title: { text: 'Nombre de parcelles', style: { color: '#6b7280', fontSize: '13px' } },
|
||||||
|
labels: { formatter: (val: number) => val.toFixed(0) }
|
||||||
|
},
|
||||||
|
colors: ['#22c55e', '#94a3b8'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: true, width: 2, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#f3f4f6', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => val + ' parcelles' } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar : enquêtées vs non enquêtées par structure ────────────────────
|
||||||
|
buildBarStructure(): void {
|
||||||
|
this.barStructureOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Enquêtées', data: this.statsParStructure.map(s => s.enquetees) },
|
||||||
|
{ name: 'Non enquêtées', data: this.statsParStructure.map(s => s.nonEnquetees) }
|
||||||
|
],
|
||||||
|
chart: { type: 'bar', height: 340, stacked: true, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 } },
|
||||||
|
plotOptions: { bar: { horizontal: true, borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParStructure.map(s => s.structure),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: { labels: { style: { colors: '#6b7280', fontSize: '12px' } } },
|
||||||
|
colors: ['#22c55e', '#94a3b8'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: false, width: 0, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#f3f4f6', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: true, style: { fontSize: '11px', fontWeight: 600, colors: ['#fff'] } },
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => val + ' parcelles' } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar : géoréférencées par commune ──────────────────────────────────
|
||||||
|
buildBarGeoParCommune(): void {
|
||||||
|
this.barGeoParCommuneOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Géoréférencées', data: this.statsParCommune.map(c => c.georeferencees) },
|
||||||
|
{ name: 'Non géoréférencées', data: this.statsParCommune.map(c => c.nonGeoreferencees) }
|
||||||
|
],
|
||||||
|
chart: { type: 'bar', height: 340, stacked: true, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 } },
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '55%', borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParCommune.map(c => c.commune),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
title: { text: 'Nombre de parcelles', style: { color: '#6b7280', fontSize: '13px' } },
|
||||||
|
labels: { formatter: (val: number) => val.toFixed(0) }
|
||||||
|
},
|
||||||
|
colors: ['#3b82f6', '#e11d48'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: false, width: 0, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#f3f4f6', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => val + ' parcelles' } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaires ───────────────────────────────────────────────────────
|
||||||
|
getTauxEnquete(): number {
|
||||||
|
const total = this.statistiquesGlobales.totalParcelles;
|
||||||
|
return total > 0 ? Math.round((this.statistiquesGlobales.parcellesEnquetees / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTauxBati(): number {
|
||||||
|
const total = this.statistiquesGlobales.totalParcelles;
|
||||||
|
return total > 0 ? Math.round((this.statistiquesGlobales.parcellesBaties / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTauxGeo(): number {
|
||||||
|
const total = this.statistiquesGlobales.totalParcelles;
|
||||||
|
return total > 0 ? Math.round((this.statistiquesGlobales.parcellesGeoreferencees / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTauxAJour(): number {
|
||||||
|
const total = this.statistiquesGlobales.totalParcelles;
|
||||||
|
return total > 0 ? Math.round((this.statistiquesGlobales.parcellesAJour / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProgressColor(percent: number): string {
|
||||||
|
if (percent >= 80) return '#22c55e';
|
||||||
|
if (percent >= 65) return '#f59e0b';
|
||||||
|
return '#ef4444';
|
||||||
|
}
|
||||||
|
|
||||||
|
getProgressLabel(percent: number): string {
|
||||||
|
if (percent >= 80) return 'Excellent';
|
||||||
|
if (percent >= 65) return 'Moyen';
|
||||||
|
return 'Faible';
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshData(): void {
|
||||||
|
this.loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/app/office/consultation/consultation-routing.module.ts
Normal file
41
src/app/office/consultation/consultation-routing.module.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { ConsultationComponent } from './consultation.component';
|
||||||
|
import { NotFoundComponent } from 'src/app/shared/not-found/not-found.component';
|
||||||
|
import { SommaireConsultationComponent } from './sommaire-consultation/sommaire-consultation.component';
|
||||||
|
import { ListParcelleConsultationComponent } from './list-parcelle-consultation/list-parcelle-consultation.component';
|
||||||
|
import { ListBatimentConsultationComponent } from './list-batiment-consultation/list-batiment-consultation.component';
|
||||||
|
import { ListUniteLogementConsultationComponent } from './list-unite-logement-consultation/list-unite-logement-consultation.component';
|
||||||
|
import { ListDonneeImpositionConsultationComponent } from './list-donnee-imposition-consultation/list-donnee-imposition-consultation.component';
|
||||||
|
import { DetailInformationParcelleComponent } from 'src/app/shared/detail-information-parcelle/detail-information-parcelle.component';
|
||||||
|
import { DetailInformationBatimentComponent } from 'src/app/shared/detail-information-batiment/detail-information-batiment.component';
|
||||||
|
import { DetailInformationUniteLogementComponent } from 'src/app/shared/detail-information-unite-logement/detail-information-unite-logement.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '', component: ConsultationComponent,
|
||||||
|
children: [
|
||||||
|
{ path: 'liste-parcelle', component: ListParcelleConsultationComponent },
|
||||||
|
{ path: 'liste-batiment', component: ListBatimentConsultationComponent },
|
||||||
|
|
||||||
|
{ path: 'liste-unite-logement', component: ListUniteLogementConsultationComponent },
|
||||||
|
|
||||||
|
{ path: 'liste-donnee-imposition', component: ListDonneeImpositionConsultationComponent },
|
||||||
|
|
||||||
|
{ path: 'sommaire-consultation', component: SommaireConsultationComponent },
|
||||||
|
|
||||||
|
{ path: 'detail-parcelle/:id', component: DetailInformationParcelleComponent },
|
||||||
|
{ path: 'detail-batiment/:id', component: DetailInformationBatimentComponent },
|
||||||
|
{ path: 'detail-unite-logement/:id', component: DetailInformationUniteLogementComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: '**', component: NotFoundComponent }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class ConsultationRoutingModule { }
|
||||||
49
src/app/office/consultation/consultation.component.html
Normal file
49
src/app/office/consultation/consultation.component.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<div class="row" style="margin-top: 75px;">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<ul nz-menu nzTheme="dark" nzMode="horizontal" class="navBar">
|
||||||
|
<li nz-menu-item style="margin-left: 6% !important;" class="text-center" (click)="goHome()">
|
||||||
|
<img src="assets/sigibe/home.svg" alt="" class="icon-home-header">
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item nzSelected routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/consultation/sommaire-consultation"> Sommaire </a>
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/consultation/liste-parcelle">Liste des parcelles </a>
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/consultation/liste-batiment"> Liste des bâtiments </a>
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/consultation/liste-unite-logement"> Liste des unités de logement </a>
|
||||||
|
</li>
|
||||||
|
<li nz-menu-item routerLinkActive="ant-menu-item-selecte">
|
||||||
|
<a routerLink="/core/consultation/liste-donnee-imposition"> Liste des données d'imposition</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row" [ngStyle]="{ backgroundColor: module ? module.color : '' }" id="formulaire">
|
||||||
|
<div class="col-md-5" style="padding: 35px;margin-bottom: 2%;">
|
||||||
|
<div style="margin-left: 16%;">
|
||||||
|
<h3 class="text-secondary"
|
||||||
|
style="font-size: 11px;text-transform: uppercase;color: rgba(255, 255, 255, 0.61) !important;">
|
||||||
|
Module Consultation </h3>
|
||||||
|
<h1 class="text-white" style="font-size: 16px;line-height: 1.5;">
|
||||||
|
Dossier en cours sur le module consultation</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5" style="padding-left: 3.1%;">
|
||||||
|
<img src="assets/sigibe/logo-mef-white.png" alt=""
|
||||||
|
style="width: 200px;margin-top: 4%;float: right;margin-right: 20%;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-left: 6%;margin-right: 6%;background-color: rgb(255 255 255 / 75%);">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
53
src/app/office/consultation/consultation.component.ts
Normal file
53
src/app/office/consultation/consultation.component.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-consultation',
|
||||||
|
templateUrl: './consultation.component.html',
|
||||||
|
styleUrls: ['./consultation.component.css']
|
||||||
|
})
|
||||||
|
export class ConsultationComponent {
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
isVisibleReference = false;
|
||||||
|
isVisibleSecteur = false;
|
||||||
|
isVisibleEquipe = false;
|
||||||
|
menuNum = 0;
|
||||||
|
|
||||||
|
module: any = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.module = JSON.parse(this.tokenStorage.getModule());
|
||||||
|
console.log(JSON.parse(this.tokenStorage.getModule()));
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
change(value: any, menuNum: number): void {
|
||||||
|
if (menuNum == 1)
|
||||||
|
this.isVisibleReference = value;
|
||||||
|
if (menuNum == 2)
|
||||||
|
this.isVisibleSecteur = value;
|
||||||
|
if (menuNum == 3)
|
||||||
|
this.isVisibleEquipe = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
goHome(): void {
|
||||||
|
this.tokenStorage.saveModule(null);
|
||||||
|
this.router.navigate(['/principale']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
src/app/office/consultation/consultation.module.ts
Normal file
40
src/app/office/consultation/consultation.module.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { ConsultationRoutingModule } from './consultation-routing.module';
|
||||||
|
import { ConsultationComponent } from './consultation.component';
|
||||||
|
import { SommaireConsultationComponent } from './sommaire-consultation/sommaire-consultation.component';
|
||||||
|
import { ListParcelleConsultationComponent } from './list-parcelle-consultation/list-parcelle-consultation.component';
|
||||||
|
import { ListBatimentConsultationComponent } from './list-batiment-consultation/list-batiment-consultation.component';
|
||||||
|
import { ListUniteLogementConsultationComponent } from './list-unite-logement-consultation/list-unite-logement-consultation.component';
|
||||||
|
import { ListDonneeImpositionConsultationComponent } from './list-donnee-imposition-consultation/list-donnee-imposition-consultation.component';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { SharedModule } from 'src/app/shared/shared.module';
|
||||||
|
import { DataTablesModule } from 'angular-datatables';
|
||||||
|
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ConsultationComponent,
|
||||||
|
SommaireConsultationComponent,
|
||||||
|
ListParcelleConsultationComponent,
|
||||||
|
ListBatimentConsultationComponent,
|
||||||
|
ListUniteLogementConsultationComponent,
|
||||||
|
ListDonneeImpositionConsultationComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ConsultationRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
SharedModule,
|
||||||
|
|
||||||
|
DataTablesModule,
|
||||||
|
NgApexchartsModule,
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
|
||||||
|
|
||||||
|
})
|
||||||
|
export class ConsultationModule { }
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,424 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<h6 class="card-title" style="font-size: 16px!important;text-transform: none;">
|
||||||
|
Liste des bâtiments <strong class="text-primary"> - (quartier sélectionné :
|
||||||
|
{{ quartierSelected ? quartierSelected.quartierNom : '-' }}) </strong>
|
||||||
|
</h6>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- ══ ARBRE ══════════════════════════════════════ -->
|
||||||
|
<div class="col-md-3" style="padding-right:0;">
|
||||||
|
<div class="arbre-container">
|
||||||
|
|
||||||
|
<div class="arbre-title">
|
||||||
|
<span nz-icon nzType="apartment" nzTheme="outline"></span>
|
||||||
|
Divisions administratives
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-tree [nzData]="nodes" nzShowLine nzShowIcon [nzTreeTemplate]="nzTreeTemplate"
|
||||||
|
(nzClick)="onNodeClick($event)" (nzExpandChange)="onNodeClick($event)">
|
||||||
|
</nz-tree>
|
||||||
|
|
||||||
|
<ng-template #nzTreeTemplate let-node>
|
||||||
|
<div class="tree-node-row">
|
||||||
|
<span class="tree-node-icon">
|
||||||
|
<span nz-icon
|
||||||
|
[nzType]="node.isLeaf ? 'environment' : (node.isExpanded ? 'folder-open' : 'folder')"
|
||||||
|
[style.color]="getBadgeColor(getNiveau(node.key))" nzTheme="outline">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="tree-node-title" [class.tree-node-leaf]="node.isLeaf"
|
||||||
|
[class.tree-node-selected]="node.isLeaf && quartierSelected?.quartierId === node.origin?.quartier?.quartierId">
|
||||||
|
{{ node.title }}
|
||||||
|
</span>
|
||||||
|
<!--<span class="tree-node-badge"
|
||||||
|
[style.background]="getBadgeColor(getNiveau(node.key))">
|
||||||
|
{{ node.origin?.nbParcelles | number:'1.0-0':'fr' }} p.
|
||||||
|
</span>-->
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div *ngIf="nodes.length === 0" class="no-data">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:28px;"></span>
|
||||||
|
<span>Aucun département trouvé</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- fin arbre -->
|
||||||
|
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<div class="formulaire p-4"
|
||||||
|
style="width: 100%; border-radius: 5px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.08);">
|
||||||
|
<div>
|
||||||
|
<h2 style="font-size: 18px;margin-bottom: -10px;">Liste des bâtiments
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<span style="background-color: #313131;font-size: 3px;margin-top:5px;">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<p style="font-size: 12px; color: #636363; margin-bottom: 30px;margin-top:5px;">
|
||||||
|
Cette interface affiche toutes les informations sur les bâtiments
|
||||||
|
enregistrés (identification, références foncières, catégorie, usage).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-container">
|
||||||
|
<!-- ── En-tête ── -->
|
||||||
|
<div class="pl-header">
|
||||||
|
<div class="pl-header-left">
|
||||||
|
<span nz-icon nzType="bank" nzTheme="outline"
|
||||||
|
style="font-size:18px;color:#1f8653;"></span>
|
||||||
|
<div>
|
||||||
|
<div class="pl-title">Bâtiments du quartier</div>
|
||||||
|
<div class="pl-count">{{ totalElements }} bâtiment(s) au total</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-header-right">
|
||||||
|
<button class="pl-btn-filter mr-1" (click)="exportPageCourante()">
|
||||||
|
<span nz-icon nzType="file-excel" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Exporter en Excel
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-filter"
|
||||||
|
[class.pl-btn-filter-active]="filterVisible || filterApplied"
|
||||||
|
(click)="toggleFiltre()">
|
||||||
|
<span nz-icon nzType="filter" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Filtres
|
||||||
|
<span class="pl-filter-badge" *ngIf="filterApplied">•</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Panneau filtres ── -->
|
||||||
|
<div class="pl-filter-panel" [class.pl-filter-panel-open]="filterVisible">
|
||||||
|
<form [formGroup]="filterForm">
|
||||||
|
<div class="pl-filter-grid">
|
||||||
|
|
||||||
|
<!-- NUB / Code -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUB</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nub"
|
||||||
|
placeholder="ex: B01">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Code bâtiment</label>
|
||||||
|
<input class="pl-filter-input" formControlName="code"
|
||||||
|
placeholder="ex: CODE-001">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Q / I / P parcelle -->
|
||||||
|
<div class="pl-filter-field"
|
||||||
|
style="display:grid;grid-template-columns:1fr 1fr;gap:6px;">
|
||||||
|
<div style="width: 90px;">
|
||||||
|
<label class="pl-filter-label">I (Îlot)</label>
|
||||||
|
<input style="width: 90px;" class="pl-filter-input" formControlName="parcelleI"
|
||||||
|
placeholder="ex: 101">
|
||||||
|
</div>
|
||||||
|
<div style="width: 90px;">
|
||||||
|
<label class="pl-filter-label">P (Parcelle)</label>
|
||||||
|
<input style="width: 90px;" class="pl-filter-input" formControlName="parcelleP"
|
||||||
|
placeholder="ex: A">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NUP -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUP Parcelle</label>
|
||||||
|
<input class="pl-filter-input" formControlName="parcelleNup"
|
||||||
|
placeholder="ex: NUP-001">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="personneNom"
|
||||||
|
placeholder="ex: CODO">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Prénom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="personnePrenom"
|
||||||
|
placeholder="ex: MICHEL">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Catégorie & Usage -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Catégorie bâtiment</label>
|
||||||
|
<select class="pl-filter-input" formControlName="categorieBatimentId">
|
||||||
|
<option value="">-- Toutes --</option>
|
||||||
|
<option *ngFor="let cat of categorieBatimentList" [value]="cat.id">
|
||||||
|
{{ cat.code }} — {{ cat.standing }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Usage</label>
|
||||||
|
<select class="pl-filter-input" formControlName="usageId">
|
||||||
|
<option value="">-- Tous --</option>
|
||||||
|
<option *ngFor="let usage of usageList" [value]="usage.id">
|
||||||
|
{{ usage.nom }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-filter-actions">
|
||||||
|
<button class="pl-btn-reset" type="button" (click)="reinitialiserFiltre()">
|
||||||
|
<span nz-icon nzType="redo" nzTheme="outline"></span>
|
||||||
|
Réinitialiser
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-apply" type="button" (click)="appliquerFiltre()">
|
||||||
|
<span nz-icon nzType="search" nzTheme="outline"></span>
|
||||||
|
Appliquer
|
||||||
|
<span *ngIf="filterApplied" style="margin-left:4px;">
|
||||||
|
({{ filteredList.length }} résultat(s))
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Loading ── -->
|
||||||
|
<div class="pl-loading" *ngIf="loading">
|
||||||
|
<nz-spin nzSimple></nz-spin>
|
||||||
|
<span>Chargement des bâtiments…</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Liste ── -->
|
||||||
|
<div *ngIf="!loading">
|
||||||
|
|
||||||
|
<div class="pl-empty" *ngIf="filteredList.length === 0">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:32px;"></span>
|
||||||
|
<p>Aucun bâtiment trouvé</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cards -->
|
||||||
|
<div class="pl-card" *ngFor="let item of pageCourante">
|
||||||
|
|
||||||
|
<div class="pl-card-main">
|
||||||
|
|
||||||
|
<!-- Identification -->
|
||||||
|
<div class="pl-col pl-col-identity">
|
||||||
|
<div class="pl-badges">
|
||||||
|
<span class="pl-badge pl-badge-qip">
|
||||||
|
Q{{ item.parcelleQ }} . {{ item.parcelleI }} .
|
||||||
|
{{ item.parcelleP }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge" *ngIf="item.categorieBatimentCode"
|
||||||
|
style="background:#e0f2fe;color:#0369a1;">
|
||||||
|
{{ item.categorieBatimentCode }}
|
||||||
|
{{ item.categorieBatimentStanding ? '— ' + item.categorieBatimentStanding : '' }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-enquete"
|
||||||
|
*ngIf="item.enqueteBatiementCourantId">
|
||||||
|
<span nz-icon nzType="file-search" nzTheme="outline"></span>
|
||||||
|
Enquêté
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-no-enquete"
|
||||||
|
*ngIf="!item.enqueteBatiementCourantId">
|
||||||
|
Non enquêté
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-nup">
|
||||||
|
NUB : {{ item.nub || '—' }} — Code : {{ item.code || '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.parcelleNup">
|
||||||
|
<span nz-icon nzType="home" nzTheme="outline"></span>
|
||||||
|
NUP Parcelle : {{ item.parcelleNup }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.dateConstruction">
|
||||||
|
<span nz-icon nzType="calendar" nzTheme="outline"></span>
|
||||||
|
Construit le : {{ item.dateConstruction | date:'dd/MM/yyyy' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Usage & Surfaces -->
|
||||||
|
<div class="pl-col pl-col-domaine">
|
||||||
|
<div class="pl-domaine-type">{{ item.usageNom || '—' }}</div>
|
||||||
|
<div class="pl-domaine-nature">{{ item.nbreUniteLogement || 0 }}
|
||||||
|
unité(s) logement</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.superficieAuSol">
|
||||||
|
<span nz-icon nzType="expand" nzTheme="outline"></span>
|
||||||
|
{{ item.superficieAuSol | number:'1.0-2':'fr' }} m² au sol
|
||||||
|
</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.nombrePiscine">
|
||||||
|
🏊 {{ item.nombrePiscine }} piscine(s)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-col pl-col-prop">
|
||||||
|
<div class="pl-prop-name">
|
||||||
|
{{ item.personneRaisonSociale
|
||||||
|
|| ((item.personneNom || '') + ' ' + (item.personnePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.valeurBatimentEstime">
|
||||||
|
Val. estimée : {{ formatMontant(item.valeurBatimentEstime) }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.montantMensuelLocation">
|
||||||
|
Loyer mensuel : {{ formatMontant(item.montantMensuelLocation) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
|
<div class="pl-col pl-col-action">
|
||||||
|
<button class="pl-btn-detail" (click)="toggleRow(item.id)">
|
||||||
|
<span nz-icon [nzType]="isExpanded(item.id) ? 'up' : 'down'"></span>
|
||||||
|
{{ isExpanded(item.id) ? 'Réduire' : 'Détails' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Détails expandés -->
|
||||||
|
<div class="pl-card-detail" *ngIf="isExpanded(item.id)">
|
||||||
|
<div class="pl-detail-grid">
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUB</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nub || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Code</span>
|
||||||
|
<span class="pl-detail-val">{{ item.code || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Date construction</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ (item.dateConstruction | date:'dd/MM/yyyy') || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Q . I . P Parcelle</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.parcelleQ }} . {{ item.parcelleI }} .
|
||||||
|
{{ item.parcelleP }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUP Parcelle</span>
|
||||||
|
<span class="pl-detail-val">{{ item.parcelleNup || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Catégorie / Standing</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.categorieBatimentCode || '—' }}
|
||||||
|
{{ item.categorieBatimentStanding ? '— ' + item.categorieBatimentStanding : '' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Usage</span>
|
||||||
|
<span class="pl-detail-val">{{ item.usageNom || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie au sol (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficieAuSol || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie louée (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficieLouee || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Nbre unités logement</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.nbreUniteLogement ?? '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Nombre piscines</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nombrePiscine ?? '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Propriétaire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.personneRaisonSociale
|
||||||
|
|| ((item.personneNom || '') + ' ' + (item.personnePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer mensuel</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.montantMensuelLocation) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel déclaré</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.montantLocatifAnnuelDeclare) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel calculé</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.montantLocatifAnnuelCalcule) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel estimé</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.montantLocatifAnnuelEstime) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. estimée bâtiment</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurBatimentEstime) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. réelle bâtiment</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurBatimentReel) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. calculée bâtiment</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurBatimentCalcule) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<button class="btn-action btn-action-person"
|
||||||
|
(click)="afficherDetail(item.id)">
|
||||||
|
<i class="mdi mdi-arrow-right"></i> Plus de détails
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin cards -->
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="pl-pagination" *ngIf="totalElements > pageSize">
|
||||||
|
<span class="pl-pagination-info">
|
||||||
|
Page {{ pageNo + 1 }} sur {{ totalPages }} — {{ totalElements }} bâtiment(s)
|
||||||
|
</span>
|
||||||
|
<nz-pagination [nzPageIndex]="pageNo + 1" [nzTotal]="totalElements"
|
||||||
|
[nzPageSize]="pageSize" [nzShowSizeChanger]="true"
|
||||||
|
[nzPageSizeOptions]="[10, 20, 50, 100]"
|
||||||
|
(nzPageIndexChange)="onPageChange($event)"
|
||||||
|
(nzPageSizeChange)="onPageSizeChange($event)">
|
||||||
|
</nz-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
import { Component, SimpleChanges, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { ExcelExportService } from 'src/app/excel-export.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
export interface BatimentPagedItem {
|
||||||
|
id?: number;
|
||||||
|
nub?: string;
|
||||||
|
code?: string;
|
||||||
|
dateConstruction?: string;
|
||||||
|
parcelleId?: number;
|
||||||
|
parcelleNup?: string;
|
||||||
|
parcelleQ?: string;
|
||||||
|
parcelleI?: string;
|
||||||
|
parcelleP?: string;
|
||||||
|
personneId?: number;
|
||||||
|
personneNom?: string;
|
||||||
|
personnePrenom?: string;
|
||||||
|
personneRaisonSociale?: string;
|
||||||
|
superficieAuSol?: number;
|
||||||
|
superficieLouee?: number;
|
||||||
|
enqueteBatiementCourantId?: number;
|
||||||
|
categorieBatimentId?: number;
|
||||||
|
categorieBatimentCode?: string;
|
||||||
|
categorieBatimentStanding?: string;
|
||||||
|
nombrePiscine?: number;
|
||||||
|
montantLocatifAnnuelDeclare?: number;
|
||||||
|
montantLocatifAnnuelCalcule?: number;
|
||||||
|
montantLocatifAnnuelEstime?: number;
|
||||||
|
valeurBatimentEstime?: number;
|
||||||
|
valeurBatimentReel?: number;
|
||||||
|
valeurBatimentCalcule?: number;
|
||||||
|
montantMensuelLocation?: number;
|
||||||
|
usageId?: number;
|
||||||
|
usageNom?: string;
|
||||||
|
nbreUniteLogement?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list-batiment-consultation',
|
||||||
|
templateUrl: './list-batiment-consultation.component.html',
|
||||||
|
styleUrls: ['./list-batiment-consultation.component.css']
|
||||||
|
})
|
||||||
|
export class ListBatimentConsultationComponent {
|
||||||
|
|
||||||
|
// ── Données ───────────────────────────────────────────
|
||||||
|
donnees: BatimentPagedItem[] = [];
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
// ── Pagination client-side ────────────────────────────
|
||||||
|
pageNo = 0;
|
||||||
|
pageSize = 10;
|
||||||
|
|
||||||
|
// ── Filtre ────────────────────────────────────────────
|
||||||
|
filterForm!: FormGroup;
|
||||||
|
filterVisible = false;
|
||||||
|
filterApplied = false;
|
||||||
|
|
||||||
|
// ── Ligne expandée ────────────────────────────────────
|
||||||
|
expandedIds = new Set<number>();
|
||||||
|
|
||||||
|
// ── Référentiels ──────────────────────────────────────
|
||||||
|
usageList: any[] = [];
|
||||||
|
categorieBatimentList: any[] = [];
|
||||||
|
|
||||||
|
// ── EXPORT LABELS ─────────────────────────────────────
|
||||||
|
private readonly EXPORT_LABELS: { [key: string]: string } = {
|
||||||
|
id: 'ID Bâtiment',
|
||||||
|
nub: 'N° Bâtiment (NUB)',
|
||||||
|
code: 'Code Bâtiment',
|
||||||
|
dateConstruction: 'Date Construction',
|
||||||
|
parcelleId: 'ID Parcelle',
|
||||||
|
parcelleNup: 'NUP Parcelle',
|
||||||
|
parcelleQ: 'Q (Quartier)',
|
||||||
|
parcelleI: 'I (Îlot)',
|
||||||
|
parcelleP: 'P (Parcelle)',
|
||||||
|
personneId: 'ID Propriétaire',
|
||||||
|
personneNom: 'Nom Propriétaire',
|
||||||
|
personnePrenom: 'Prénom Propriétaire',
|
||||||
|
personneRaisonSociale: 'Raison Sociale Propriétaire',
|
||||||
|
superficieAuSol: 'Superficie au Sol (m²)',
|
||||||
|
superficieLouee: 'Superficie Louée (m²)',
|
||||||
|
enqueteBatiementCourantId: 'ID Enquête Courante',
|
||||||
|
categorieBatimentId: 'ID Catégorie Bâtiment',
|
||||||
|
categorieBatimentCode: 'Code Catégorie Bâtiment',
|
||||||
|
categorieBatimentStanding: 'Standing Bâtiment',
|
||||||
|
nombrePiscine: 'Nombre Piscines',
|
||||||
|
montantLocatifAnnuelDeclare: 'Loyer Annuel Déclaré (FCFA)',
|
||||||
|
montantLocatifAnnuelCalcule: 'Loyer Annuel Calculé (FCFA)',
|
||||||
|
montantLocatifAnnuelEstime: 'Loyer Annuel Estimé (FCFA)',
|
||||||
|
valeurBatimentEstime: 'Valeur Estimée Bâtiment (FCFA)',
|
||||||
|
valeurBatimentReel: 'Valeur Réelle Bâtiment (FCFA)',
|
||||||
|
valeurBatimentCalcule: 'Valeur Calculée Bâtiment (FCFA)',
|
||||||
|
montantMensuelLocation: 'Loyer Mensuel (FCFA)',
|
||||||
|
usageId: 'ID Usage',
|
||||||
|
usageNom: 'Usage',
|
||||||
|
nbreUniteLogement: 'Nbre Unités Logement',
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly CHAMPS_EXCLUS = new Set([
|
||||||
|
'id', 'parcelleId', 'personneId',
|
||||||
|
'enqueteBatiementCourantId', 'categorieBatimentId', 'usageId',
|
||||||
|
]);
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
numMenu = 2;
|
||||||
|
|
||||||
|
nodes: NzTreeNodeOptions[] = []; // Les noeuds de l'arbre
|
||||||
|
|
||||||
|
arbreUtilisateurCourant: any[] = [];
|
||||||
|
quartierSelected: any = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private modalService: NzModalService,
|
||||||
|
private viewContainerRef: ViewContainerRef,
|
||||||
|
private excelExportService: ExcelExportService,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
if (this.user) {
|
||||||
|
this.crudService.getAll('secteur-decoupage/arbre/user-id/' + this.user?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.arbreUtilisateurCourant = data.object ? data.object : [];
|
||||||
|
if (this.arbreUtilisateurCourant && this.arbreUtilisateurCourant.length > 0) {
|
||||||
|
this.constructionArbreUtilisateurs();
|
||||||
|
}
|
||||||
|
this.message.success(`Chargement des découpages de l'utilisateur ${this.user?.nom} réussi`);
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
this.message.error(`Chargement des découpages de l'utilisateur ${this.user?.nom} échoué`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initFilterForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes['quartierId'] && this.quartierSelected?.quartierId) {
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.reinitialiserFiltre();
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaire ────────────────────────────────────────
|
||||||
|
constructionArbreUtilisateurs() {
|
||||||
|
const data: any[] = this.arbreUtilisateurCourant;
|
||||||
|
// Grouper par département
|
||||||
|
const deptMap = new Map<number, any>();
|
||||||
|
|
||||||
|
data.forEach(item => {
|
||||||
|
if (!deptMap.has(item.departementId)) {
|
||||||
|
deptMap.set(item.departementId, {
|
||||||
|
...item,
|
||||||
|
communes: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const dept = deptMap.get(item.departementId);
|
||||||
|
|
||||||
|
if (!dept.communes.has(item.communeId)) {
|
||||||
|
dept.communes.set(item.communeId, {
|
||||||
|
...item,
|
||||||
|
arrondissements: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const commune = dept.communes.get(item.communeId);
|
||||||
|
|
||||||
|
if (!commune.arrondissements.has(item.arrondissementId)) {
|
||||||
|
commune.arrondissements.set(item.arrondissementId, {
|
||||||
|
...item,
|
||||||
|
quartiers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const arr = commune.arrondissements.get(item.arrondissementId);
|
||||||
|
arr.quartiers.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construire les noeuds
|
||||||
|
this.nodes = Array.from(deptMap.values()).map(dept => ({
|
||||||
|
title: `${dept.departementCode} — ${dept.departementNom}`,
|
||||||
|
key: `dept-${dept.departementId}`,
|
||||||
|
icon: 'bank',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: dept.nbParcellesDepartement,
|
||||||
|
children: Array.from(dept.communes.values()).map((comm: any) => ({
|
||||||
|
title: `${comm.communeCode} — ${comm.communeNom}`,
|
||||||
|
key: `comm-${comm.communeId}`,
|
||||||
|
icon: 'home',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: comm.nbParcellesCommune,
|
||||||
|
children: Array.from(comm.arrondissements.values()).map((arr: any) => ({
|
||||||
|
title: arr.arrondissementNom,
|
||||||
|
key: `arr-${arr.arrondissementId}`,
|
||||||
|
icon: 'apartment',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: arr.nbParcellesArrondissement,
|
||||||
|
children: arr.quartiers.map((quart: any) => ({
|
||||||
|
title: quart.quartierNom,
|
||||||
|
key: `quart-${quart.quartierId}`,
|
||||||
|
icon: 'environment',
|
||||||
|
isLeaf: true,
|
||||||
|
nbParcelles: quart.nbParcellesQuartier,
|
||||||
|
quartier: quart
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(event: any) {
|
||||||
|
const node = event.node;
|
||||||
|
if (node.isLeaf && node.key.startsWith('quart-')) {
|
||||||
|
this.quartierSelected = node.origin.quartier;
|
||||||
|
console.log(' this.quartierSelected ==>', this.quartierSelected);
|
||||||
|
this.message.create('success', `Quartier ${this.quartierSelected.quartierNom} sélectionné.`);
|
||||||
|
// votre logique de sélection...
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBadgeColor(niveau: string): string {
|
||||||
|
const colors: any = {
|
||||||
|
'dept': '#204e10',
|
||||||
|
'comm': '#10b981',
|
||||||
|
'arr': '#f59e0b',
|
||||||
|
'quart': '#ef6972'
|
||||||
|
};
|
||||||
|
return colors[niveau] ?? '#6b7280';
|
||||||
|
}
|
||||||
|
|
||||||
|
getNiveau(key: string): string {
|
||||||
|
if (key.startsWith('dept')) return 'dept';
|
||||||
|
if (key.startsWith('comm')) return 'comm';
|
||||||
|
if (key.startsWith('arr')) return 'arr';
|
||||||
|
if (key.startsWith('quart')) return 'quart';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init formulaire de filtre ─────────────────────────
|
||||||
|
initFilterForm(): void {
|
||||||
|
this.crudService.getAll('usage/all').subscribe((data: any) => {
|
||||||
|
this.usageList = data.object ?? [];
|
||||||
|
});
|
||||||
|
this.crudService.getAll('categorie-batiment/all').subscribe((data: any) => {
|
||||||
|
this.categorieBatimentList = data.object ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
nub: [null],
|
||||||
|
code: [null],
|
||||||
|
parcelleNup: [null],
|
||||||
|
parcelleQ: [null],
|
||||||
|
parcelleI: [null],
|
||||||
|
parcelleP: [null],
|
||||||
|
personneNom: [null],
|
||||||
|
personnePrenom: [null],
|
||||||
|
personneRaisonSociale: [null],
|
||||||
|
categorieBatimentId: [null],
|
||||||
|
usageId: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chargement ────────────────────────────────────────
|
||||||
|
charger(): void {
|
||||||
|
if (!this.quartierSelected) return;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const url = `batiment/all/by-quartier-id/${this.quartierSelected?.quartierId}`;
|
||||||
|
|
||||||
|
this.crudService.getAll(url).subscribe({
|
||||||
|
next: (data: any) => {
|
||||||
|
this.donnees = data?.object ?? [];
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.message.error('Erreur lors du chargement des bâtiments.');
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Filtre client-side ────────────────────────────────
|
||||||
|
get filteredList(): BatimentPagedItem[] {
|
||||||
|
if (!this.filterApplied) return this.donnees;
|
||||||
|
|
||||||
|
const f = this.filterForm.value;
|
||||||
|
|
||||||
|
const match = (
|
||||||
|
filterVal: string | null | undefined,
|
||||||
|
itemVal: string | null | undefined
|
||||||
|
): boolean => {
|
||||||
|
if (!filterVal?.trim()) return true;
|
||||||
|
if (!itemVal?.trim()) return false;
|
||||||
|
return itemVal.toLowerCase().includes(filterVal.trim().toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.donnees.filter(b =>
|
||||||
|
match(f.nub, b.nub) &&
|
||||||
|
match(f.code, b.code) &&
|
||||||
|
match(f.parcelleNup, b.parcelleNup) &&
|
||||||
|
match(f.parcelleQ, b.parcelleQ) &&
|
||||||
|
match(f.parcelleI, b.parcelleI) &&
|
||||||
|
match(f.parcelleP, b.parcelleP) &&
|
||||||
|
(
|
||||||
|
match(f.personneNom, b.personneNom) ||
|
||||||
|
match(f.personneNom, b.personneRaisonSociale)
|
||||||
|
) &&
|
||||||
|
match(f.personnePrenom, b.personnePrenom) &&
|
||||||
|
(!f.categorieBatimentId || b.categorieBatimentId?.toString() === f.categorieBatimentId) &&
|
||||||
|
(!f.usageId || b.usageId?.toString() === f.usageId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageCourante(): BatimentPagedItem[] {
|
||||||
|
const debut = this.pageNo * this.pageSize;
|
||||||
|
return this.filteredList.slice(debut, debut + this.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalElements(): number { return this.filteredList.length; }
|
||||||
|
|
||||||
|
get totalPages(): number {
|
||||||
|
return this.pageSize > 0 ? Math.ceil(this.totalElements / this.pageSize) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageChange(page: number): void { this.pageNo = page - 1; }
|
||||||
|
|
||||||
|
onPageSizeChange(size: number): void {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appliquerFiltre(): void {
|
||||||
|
this.filterApplied = true;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinitialiserFiltre(): void {
|
||||||
|
this.filterForm?.reset();
|
||||||
|
this.filterApplied = false;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFiltre(): void { this.filterVisible = !this.filterVisible; }
|
||||||
|
|
||||||
|
toggleRow(id: number | undefined): void {
|
||||||
|
if (id == null) return;
|
||||||
|
this.expandedIds.has(id) ? this.expandedIds.delete(id) : this.expandedIds.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded(id: number | undefined): boolean {
|
||||||
|
return id != null && this.expandedIds.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatMontant(val: number | null | undefined): string {
|
||||||
|
if (val == null) return '—';
|
||||||
|
return new Intl.NumberFormat('fr-FR').format(val) + ' FCFA';
|
||||||
|
}
|
||||||
|
|
||||||
|
private nettoyerLigneExport(item: any): { [label: string]: any } {
|
||||||
|
const ligne: { [label: string]: any } = {};
|
||||||
|
for (const key of Object.keys(this.EXPORT_LABELS)) {
|
||||||
|
if (this.CHAMPS_EXCLUS.has(key)) continue;
|
||||||
|
const val = item[key];
|
||||||
|
ligne[this.EXPORT_LABELS[key]] =
|
||||||
|
typeof val === 'boolean' ? (val ? 'OUI' : 'NON') :
|
||||||
|
val == null ? '—' : val;
|
||||||
|
}
|
||||||
|
return ligne;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportPageCourante(): void {
|
||||||
|
if (!this.filteredList.length) {
|
||||||
|
this.message.warning('Aucune donnée à exporter.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = this.filteredList.map(item => this.nettoyerLigneExport(item));
|
||||||
|
this.excelExportService.exportAsExcelFile(data, 'batiments', 'Bâtiments');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
afficherDetail(id: any): void {
|
||||||
|
this.router.navigate(['/core/consultation/detail-batiment/' + id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,689 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<h6 class="card-title" style="font-size: 16px!important;text-transform: none;">
|
||||||
|
Liste des données d'imposition
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- ══ ARBRE ══════════════════════════════════════ -->
|
||||||
|
<div class="col-md-3" style="padding-right:0;">
|
||||||
|
<div class="arbre-container" style="min-height: auto;">
|
||||||
|
<div class="arbre-title">
|
||||||
|
<span nz-icon nzType="calendar" nzTheme="outline"></span>
|
||||||
|
Centre des impôts
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="did-floating-label-content mt-1">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Sélectionner le centre"
|
||||||
|
[(ngModel)]="structure" [compareWith]="compareFn"
|
||||||
|
(ngModelChange)="changeStructure($event)">
|
||||||
|
<nz-option *ngFor="let s of structureList" [nzLabel]="s.nom"
|
||||||
|
[nzValue]="s">
|
||||||
|
</nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top:-15px;">
|
||||||
|
Centre des impôts <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="did-floating-label-content mt-1">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Sélectionner le centre"
|
||||||
|
[(ngModel)]="exercice" [compareWith]="compareFn"
|
||||||
|
(ngModelChange)="changeExercice($event)">
|
||||||
|
<nz-option *ngFor="let s of exerciceList" [nzLabel]="s.annee"
|
||||||
|
[nzValue]="s">
|
||||||
|
</nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top:-15px;">
|
||||||
|
Exercice fiscale <span class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="arbre-container mt-3" style="min-height: 40%;">
|
||||||
|
|
||||||
|
<div class="arbre-title">
|
||||||
|
<span nz-icon nzType="apartment" nzTheme="outline"></span>
|
||||||
|
Divisions administratives
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-tree [nzData]="nodes" nzShowLine nzShowIcon [nzTreeTemplate]="nzTreeTemplate"
|
||||||
|
(nzClick)="onNodeClick($event)" (nzExpandChange)="onNodeClick($event)">
|
||||||
|
</nz-tree>
|
||||||
|
|
||||||
|
<ng-template #nzTreeTemplate let-node>
|
||||||
|
<div class="tree-node-row">
|
||||||
|
<span class="tree-node-icon">
|
||||||
|
<span nz-icon
|
||||||
|
[nzType]="node.isLeaf ? 'environment' : (node.isExpanded ? 'folder-open' : 'folder')"
|
||||||
|
[style.color]="getBadgeColor(getNiveau(node.key))" nzTheme="outline">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="tree-node-title" [class.tree-node-leaf]="node.isLeaf"
|
||||||
|
[class.tree-node-selected]="node.isLeaf && quartierSelected?.quartierId === node.origin?.quartier?.quartierId">
|
||||||
|
{{ node.title }}
|
||||||
|
</span>
|
||||||
|
<!--<span class="tree-node-badge"
|
||||||
|
[style.background]="getBadgeColor(getNiveau(node.key))">
|
||||||
|
{{ node.origin?.nbParcelles | number:'1.0-0':'fr' }} p.
|
||||||
|
</span>-->
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div *ngIf="nodes.length === 0" class="no-data">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:28px;"></span>
|
||||||
|
<span>Aucun département trouvé</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- fin arbre -->
|
||||||
|
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<div class="formulaire p-4"
|
||||||
|
style="width: 100%; border-radius: 5px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.08);">
|
||||||
|
<div>
|
||||||
|
<span class="quartier-pill quartier-pill" *ngIf="structure">
|
||||||
|
<span nz-icon nzType="home" nzTheme="outline"
|
||||||
|
style="margin-top: -5px;"></span>
|
||||||
|
<span class="context-chip-label"></span>
|
||||||
|
{{ structure?.nom || '-' }}
|
||||||
|
</span>
|
||||||
|
<span class="quartier-pill quartier-pill ml-2" *ngIf="exercice">
|
||||||
|
<span nz-icon nzType="calendar" nzTheme="outline"
|
||||||
|
style="margin-top: -5px;"></span>
|
||||||
|
<span class="context-chip-label"> </span>
|
||||||
|
{{ exercice?.annee || '-' }}
|
||||||
|
</span>
|
||||||
|
<!-- Parcelle sélectionnée -->
|
||||||
|
<span class="quartier-pill quartier-pill-empty ml-2" *ngIf="quartierSelected">
|
||||||
|
<span nz-icon nzType="environment" nzTheme="outline"
|
||||||
|
style="margin-top: -5px;"></span>
|
||||||
|
<span class="context-chip-label"> </span>
|
||||||
|
{{ quartierSelected?.quartierNom || '-' }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2 style="font-size: 18px;margin-bottom: -10px;" class="mt-3">Liste des données d'imposition
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<span style="background-color: #313131;font-size: 3px;margin-top:5px;">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<p style="font-size: 12px; color: #636363; margin-bottom: 30px;margin-top:5px;">
|
||||||
|
Cette interface affiche toutes les données permettant d'éditer les avis d'imposition
|
||||||
|
(nature d'impôt, montant de l'impôt, contribuable, etc).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="pl-container">
|
||||||
|
|
||||||
|
<!-- ── En-tête ── -->
|
||||||
|
<div class="pl-header">
|
||||||
|
<div class="pl-header-left">
|
||||||
|
<span nz-icon nzType="dollar-circle" nzTheme="outline"
|
||||||
|
style="font-size:18px;color:#1f8653;"></span>
|
||||||
|
<div>
|
||||||
|
<div class="pl-title">Données d'impositions</div>
|
||||||
|
<div class="pl-count">{{ totalElements }} imposition(s) au total</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-header-right">
|
||||||
|
<button class="pl-btn-filter mr-1" (click)="exportPageCourante()">
|
||||||
|
<span nz-icon nzType="file-excel" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Exporter en Excel
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-filter"
|
||||||
|
[class.pl-btn-filter-active]="filterVisible || filterApplied"
|
||||||
|
(click)="toggleFiltre()">
|
||||||
|
<span nz-icon nzType="filter" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Filtres
|
||||||
|
<span class="pl-filter-badge" *ngIf="filterApplied">•</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Panneau filtres ── -->
|
||||||
|
<div class="pl-filter-panel" [class.pl-filter-panel-open]="filterVisible">
|
||||||
|
<form [formGroup]="filterForm">
|
||||||
|
<div class="pl-filter-grid">
|
||||||
|
|
||||||
|
<!-- Identification parcelle -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUP</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nup"
|
||||||
|
placeholder="ex: NUP-001">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Titre Foncier</label>
|
||||||
|
<input class="pl-filter-input" formControlName="titreFoncier"
|
||||||
|
placeholder="ex: TF-2023">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field"
|
||||||
|
style="display:grid;grid-template-columns:1fr 1fr;gap:6px;">
|
||||||
|
<div style="width: 100px;">
|
||||||
|
<label class="pl-filter-label">Îlot</label>
|
||||||
|
<input style="width: 100px;" class="pl-filter-input"
|
||||||
|
formControlName="ilot" placeholder="ex: 101">
|
||||||
|
</div>
|
||||||
|
<div style="width: 100px;">
|
||||||
|
<label class="pl-filter-label">Parcelle</label>
|
||||||
|
<input style="width: 100px;" class="pl-filter-input"
|
||||||
|
formControlName="parcelle" placeholder="ex: A">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nomProp"
|
||||||
|
placeholder="ex: CODO">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Prénom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="prenomProp"
|
||||||
|
placeholder="ex: MICHEL">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">IFU</label>
|
||||||
|
<input class="pl-filter-input" formControlName="ifu"
|
||||||
|
placeholder="ex: 011056">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NPI</label>
|
||||||
|
<input class="pl-filter-input" formControlName="npi"
|
||||||
|
placeholder="ex: NPI-001">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Localisation -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Quartier / Village</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nomQuartierVillage"
|
||||||
|
placeholder="ex: Bocossi">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Exercice & Nature -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Année</label>
|
||||||
|
<input class="pl-filter-input" type="number" formControlName="annee"
|
||||||
|
placeholder="ex: 2025">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nature impôt</label>
|
||||||
|
<select class="pl-filter-input" formControlName="natureImpot">
|
||||||
|
<option value="">-- Tous --</option>
|
||||||
|
<option value="TFU">TFU</option>
|
||||||
|
<option value="TP">TP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bâtie / Exonérée -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Bâtie</label>
|
||||||
|
<select class="pl-filter-input" formControlName="batie">
|
||||||
|
<option [ngValue]="null">-- Toutes --</option>
|
||||||
|
<option [ngValue]="true">Oui</option>
|
||||||
|
<option [ngValue]="false">Non</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Exonérée</label>
|
||||||
|
<select class="pl-filter-input" formControlName="exonere">
|
||||||
|
<option [ngValue]="null">-- Toutes --</option>
|
||||||
|
<option [ngValue]="true">Oui</option>
|
||||||
|
<option [ngValue]="false">Non</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-filter-actions">
|
||||||
|
<button class="pl-btn-reset" type="button" (click)="reinitialiserFiltre()">
|
||||||
|
<span nz-icon nzType="redo" nzTheme="outline"></span>
|
||||||
|
Réinitialiser
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-apply" type="button" (click)="appliquerFiltre()">
|
||||||
|
<span nz-icon nzType="search" nzTheme="outline"></span>
|
||||||
|
Appliquer
|
||||||
|
<span *ngIf="filterApplied" style="margin-left:4px;">
|
||||||
|
({{ filteredList.length }} résultat(s))
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Loading ── -->
|
||||||
|
<div class="pl-loading" *ngIf="loading">
|
||||||
|
<nz-spin nzSimple></nz-spin>
|
||||||
|
<span>Chargement des impositions…</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Liste ── -->
|
||||||
|
<div *ngIf="!loading">
|
||||||
|
|
||||||
|
<div class="pl-empty" *ngIf="filteredList.length === 0">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:32px;"></span>
|
||||||
|
<p>Aucune imposition trouvée</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cards -->
|
||||||
|
<div class="pl-card" *ngFor="let item of pageCourante">
|
||||||
|
|
||||||
|
<div class="pl-card-main">
|
||||||
|
|
||||||
|
<!-- Identification -->
|
||||||
|
<div class="pl-col pl-col-identity">
|
||||||
|
<div class="pl-badges">
|
||||||
|
<span class="pl-badge pl-badge-qip">
|
||||||
|
Q{{ item.q }} . {{ item.ilot }} . {{ item.parcelle }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge"
|
||||||
|
[style.background]="item.batie ? '#dcfce7' : '#fef3c7'"
|
||||||
|
[style.color]="item.batie ? '#166534' : '#92400e'">
|
||||||
|
{{ item.batie ? 'Bâtie' : 'Non bâtie' }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge" *ngIf="item.exonere"
|
||||||
|
style="background:#f3e8ff;color:#7e22ce;">
|
||||||
|
Exonérée
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge" style="background:#e0f2fe;color:#0369a1;"
|
||||||
|
*ngIf="item.natureImpot">
|
||||||
|
{{ item.natureImpot }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-nup">
|
||||||
|
{{ item.nup || item.nupProvisoire || '— NUP non défini' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.titreFoncier">
|
||||||
|
<span nz-icon nzType="file-protect" nzTheme="outline"></span>
|
||||||
|
TF : {{ item.titreFoncier }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line">
|
||||||
|
<span nz-icon nzType="environment" nzTheme="outline"></span>
|
||||||
|
{{ item.nomQuartierVillage || '—' }}
|
||||||
|
{{ item.annee ? '— ' + item.annee : '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fiscalité -->
|
||||||
|
<div class="pl-col pl-col-domaine">
|
||||||
|
<div class="pl-domaine-type">
|
||||||
|
Taxe : {{ formatMontant(item.montantTaxe) }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-domaine-nature">
|
||||||
|
Loyer annuel : {{ formatMontant(item.montantLoyerAnnuel) }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.superficieParc">
|
||||||
|
<span nz-icon nzType="expand" nzTheme="outline"></span>
|
||||||
|
{{ item.superficieParc | number:'1.0-2':'fr' }} m²
|
||||||
|
</div>
|
||||||
|
<div class="pl-domaine-nature" *ngIf="item.zoneRfu?.nom">
|
||||||
|
Zone RFU : {{ item.zoneRfu?.nom }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-col pl-col-prop">
|
||||||
|
<div class="pl-prop-name">
|
||||||
|
{{ item.raisonSociale
|
||||||
|
|| ((item.nomProp || '') + ' ' + (item.prenomProp || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.ifu">
|
||||||
|
IFU : {{ item.ifu.trim() }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.npi">
|
||||||
|
NPI : {{ item.npi }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.telProp">
|
||||||
|
<span nz-icon nzType="phone" nzTheme="outline"></span>
|
||||||
|
{{ item.telProp }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
|
<div class="pl-col pl-col-action">
|
||||||
|
<button class="pl-btn-detail" (click)="toggleRow(item.id)">
|
||||||
|
<span nz-icon [nzType]="isExpanded(item.id) ? 'up' : 'down'"></span>
|
||||||
|
{{ isExpanded(item.id) ? 'Réduire' : 'Détails' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Détails expandés -->
|
||||||
|
<div class="pl-card-detail" *ngIf="isExpanded(item.id)">
|
||||||
|
<div class="pl-detail-grid">
|
||||||
|
|
||||||
|
<!-- Localisation -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Département</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.codeDepartement }} — {{ item.nomDepartement || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Commune</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.codeCommune }} — {{ item.nomCommune || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Arrondissement</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.codeArrondissement }} —
|
||||||
|
{{ item.nomArrondissement || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Quartier / Village</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.codeQuartierVillage }} —
|
||||||
|
{{ item.nomQuartierVillage || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Q . Îlot . Parcelle</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.q }} . {{ item.ilot }} . {{ item.parcelle }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUP / NUP Provisoire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.nup || '—' }} / {{ item.nupProvisoire || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Titre Foncier</span>
|
||||||
|
<span class="pl-detail-val">{{ item.titreFoncier || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Zone RFU</span>
|
||||||
|
<span class="pl-detail-val">{{ item.zoneRfu?.nom || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Longitude / Latitude</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.longitude ? (item.longitude + ' / ' + item.latitude) : '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Propriétaire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.raisonSociale
|
||||||
|
|| ((item.nomProp || '') + ' ' + (item.prenomProp || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">IFU / NPI</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.ifu?.trim() || '—' }} / {{ item.npi || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Tél. / Email prop.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.telProp || '—' }} / {{ item.emailProp || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Adresse prop.</span>
|
||||||
|
<span class="pl-detail-val">{{ item.adresseProp || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Signataire -->
|
||||||
|
<div class="pl-detail-item" *ngIf="item.nomSc">
|
||||||
|
<span class="pl-detail-label">Signataire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.nomSc }} {{ item.prenomSc }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item" *ngIf="item.telSc">
|
||||||
|
<span class="pl-detail-label">Tél. signataire</span>
|
||||||
|
<span class="pl-detail-val">{{ item.telSc }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Caractéristiques -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Bâtie</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<span class="pl-badge"
|
||||||
|
[ngClass]="item.batie ? 'badge-success' : 'badge-warning'">
|
||||||
|
{{ item.batie ? 'OUI' : 'NON' }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Exonérée</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<span class="pl-badge"
|
||||||
|
[ngClass]="item.exonere ? 'badge-danger' : 'badge-success'">
|
||||||
|
{{ item.exonere ? 'OUI' : 'NON' }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Bâtiment exonéré</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<span class="pl-badge"
|
||||||
|
[ngClass]="item.batimentExonere ? 'badge-danger' : 'badge-success'">
|
||||||
|
{{ item.batimentExonere ? 'OUI' : 'NON' }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">U.L. exonérée</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<span class="pl-badge"
|
||||||
|
[ngClass]="item.uniteLogementExonere ? 'badge-danger' : 'badge-success'">
|
||||||
|
{{ item.uniteLogementExonere ? 'OUI' : 'NON' }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Catégorie bâtiment</span>
|
||||||
|
<span class="pl-detail-val">{{ item.categorieBat || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Standing</span>
|
||||||
|
<span class="pl-detail-val">{{ item.standingBat || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Usage</span>
|
||||||
|
<span class="pl-detail-val">{{ item.categorieUsage || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">N° Bâtiment / N° U.L.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.numBatiment || '—' }} /
|
||||||
|
{{ item.numUniteLogement || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Nbre bâtiments / U.L. /
|
||||||
|
Piscines</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.nombreBat ?? '—' }} / {{ item.nombreUlog ?? '—' }} /
|
||||||
|
{{ item.nombrePiscine ?? '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Surfaces -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Sup. parcelle (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficieParc || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Sup. sol bâtiment (m²)</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.superficieAuSolBat || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Sup. sol U.L. (m²)</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.superficieAuSolUlog || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Valeurs fiscales -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Montant Taxe</span>
|
||||||
|
<span class="pl-detail-val accent">
|
||||||
|
{{ formatMontant(item.montantTaxe) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Taux TFU (%)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.tauxTfu ?? '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">TFU Minimum</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.tfuMinimum) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">TFU / m²</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.tfuMetreCarre) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.montantLoyerAnnuel) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Loc. Adm.</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurLocativeAdm) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Loc. Adm. / m²</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurLocativeAdmMetreCarre) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Valeur bâtiment</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurBatiment) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Valeur parcelle</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurParcelle) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Adm. Parc. NB</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.valeurAdminParcelleNb) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Adm. Parc. NB / m²</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurAdminParcelleNbMetreCarre) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Date enquête</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ (item.dateEnquete | date:'dd/MM/yyyy') || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Champs réintégrés ── -->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Zone RFU</span>
|
||||||
|
<span class="pl-detail-val">{{ item.zoneRfu?.nom || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Loc. Adm. Taux Prop. Parc.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurLocativeAdmTauxPropParc) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. Loc. Adm. Sup. Réelle</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurLocativeAdmSupReel) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie Sol Taux Prop. Parc.
|
||||||
|
(m²)</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.superficieAuSolTauxPropParc || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">TFU Calculé Taux Prop. Parc.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.tfuCalculeTauxPropParc) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">TFU Superficie Sol Réelle</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.tfuSuperficieAuSolReel) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">TFU Piscine</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ formatMontant(item.tfuPiscine) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin cards -->
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="pl-pagination" *ngIf="totalElements > pageSize">
|
||||||
|
<span class="pl-pagination-info">
|
||||||
|
Page {{ pageNo + 1 }} sur {{ totalPages }}
|
||||||
|
— {{ totalElements }} imposition(s)
|
||||||
|
</span>
|
||||||
|
<nz-pagination [nzPageIndex]="pageNo + 1" [nzTotal]="totalElements"
|
||||||
|
[nzPageSize]="pageSize" [nzShowSizeChanger]="true"
|
||||||
|
[nzPageSizeOptions]="[10, 20, 50, 100]"
|
||||||
|
(nzPageIndexChange)="onPageChange($event)"
|
||||||
|
(nzPageSizeChange)="onPageSizeChange($event)">
|
||||||
|
</nz-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,538 @@
|
|||||||
|
import { Component, SimpleChanges, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { ExcelExportService } from 'src/app/excel-export.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
export interface ImpositionPagedItem {
|
||||||
|
externalKey?: number;
|
||||||
|
enqueteExternalKey?: number;
|
||||||
|
id?: number;
|
||||||
|
annee?: number;
|
||||||
|
codeDepartement?: string;
|
||||||
|
nomDepartement?: string;
|
||||||
|
codeCommune?: string;
|
||||||
|
nomCommune?: string;
|
||||||
|
codeArrondissement?: string;
|
||||||
|
nomArrondissement?: string;
|
||||||
|
codeQuartierVillage?: string;
|
||||||
|
nomQuartierVillage?: string;
|
||||||
|
q?: string;
|
||||||
|
ilot?: string;
|
||||||
|
parcelle?: string;
|
||||||
|
nup?: string;
|
||||||
|
nupProvisoire?: string;
|
||||||
|
titreFoncier?: string;
|
||||||
|
numBatiment?: string;
|
||||||
|
numUniteLogement?: string;
|
||||||
|
ifu?: string;
|
||||||
|
npi?: string;
|
||||||
|
telProp?: string;
|
||||||
|
emailProp?: string;
|
||||||
|
nomProp?: string;
|
||||||
|
prenomProp?: string;
|
||||||
|
raisonSociale?: string;
|
||||||
|
adresseProp?: string;
|
||||||
|
telSc?: string;
|
||||||
|
emailSc?: string;
|
||||||
|
nomSc?: string;
|
||||||
|
prenomSc?: string;
|
||||||
|
adresseSc?: string;
|
||||||
|
longitude?: string;
|
||||||
|
latitude?: string;
|
||||||
|
superficieParc?: number;
|
||||||
|
superficieAuSolBat?: number;
|
||||||
|
superficieAuSolLoue?: number;
|
||||||
|
superficieAuSolUlog?: number;
|
||||||
|
batie?: boolean;
|
||||||
|
exonere?: boolean;
|
||||||
|
batimentExonere?: boolean;
|
||||||
|
uniteLogementExonere?: boolean;
|
||||||
|
valeurLocativeAdm?: number;
|
||||||
|
valeurLocativeAdmTauxPropParc?: number;
|
||||||
|
valeurLocativeAdmSupReel?: number;
|
||||||
|
superficieAuSolTauxPropParc?: number;
|
||||||
|
valeurLocativeAdmMetreCarre?: number;
|
||||||
|
montantLoyerAnnuel?: number;
|
||||||
|
tfuMetreCarre?: number;
|
||||||
|
tfuMinimum?: number;
|
||||||
|
standingBat?: string;
|
||||||
|
categorieUsage?: string;
|
||||||
|
categorieBat?: string;
|
||||||
|
nombrePiscine?: number;
|
||||||
|
nombreUlog?: number;
|
||||||
|
nombreBat?: number;
|
||||||
|
dateEnquete?: string;
|
||||||
|
enqueteId?: number;
|
||||||
|
secteurId?: number;
|
||||||
|
zoneRfu?: {
|
||||||
|
externalKey?: number;
|
||||||
|
enqueteExternalKey?: number;
|
||||||
|
id?: number;
|
||||||
|
code?: string;
|
||||||
|
nom?: string;
|
||||||
|
};
|
||||||
|
valeurAdminParcelleNb?: number;
|
||||||
|
tauxTfu?: number;
|
||||||
|
tfuPiscine?: number;
|
||||||
|
montantTaxe?: number;
|
||||||
|
tfuCalculeTauxPropParc?: number;
|
||||||
|
tfuSuperficieAuSolReel?: number;
|
||||||
|
valeurAdminParcelleNbMetreCarre?: number;
|
||||||
|
natureImpot?: string;
|
||||||
|
valeurBatiment?: number;
|
||||||
|
valeurParcelle?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list-donnee-imposition-consultation',
|
||||||
|
templateUrl: './list-donnee-imposition-consultation.component.html',
|
||||||
|
styleUrls: ['./list-donnee-imposition-consultation.component.css']
|
||||||
|
})
|
||||||
|
export class ListDonneeImpositionConsultationComponent {
|
||||||
|
|
||||||
|
// ── Données ───────────────────────────────────────────
|
||||||
|
donnees: ImpositionPagedItem[] = [];
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
// ── Pagination client-side ────────────────────────────
|
||||||
|
pageNo = 0;
|
||||||
|
pageSize = 10;
|
||||||
|
|
||||||
|
// ── Filtre ────────────────────────────────────────────
|
||||||
|
filterForm!: FormGroup;
|
||||||
|
filterVisible = false;
|
||||||
|
filterApplied = false;
|
||||||
|
|
||||||
|
// ── Ligne expandée ────────────────────────────────────
|
||||||
|
expandedIds = new Set<number>();
|
||||||
|
|
||||||
|
// ── EXPORT LABELS ─────────────────────────────────────
|
||||||
|
private readonly EXPORT_LABELS: { [key: string]: string } = {
|
||||||
|
annee: 'Année',
|
||||||
|
natureImpot: 'Nature Impôt',
|
||||||
|
codeDepartement: 'Code Département',
|
||||||
|
nomDepartement: 'Département',
|
||||||
|
codeCommune: 'Code Commune',
|
||||||
|
nomCommune: 'Commune',
|
||||||
|
codeArrondissement: 'Code Arrondissement',
|
||||||
|
nomArrondissement: 'Arrondissement',
|
||||||
|
codeQuartierVillage: 'Code Quartier/Village',
|
||||||
|
nomQuartierVillage: 'Quartier/Village',
|
||||||
|
q: 'Q',
|
||||||
|
ilot: 'Îlot',
|
||||||
|
parcelle: 'Parcelle',
|
||||||
|
nup: 'NUP',
|
||||||
|
nupProvisoire: 'NUP Provisoire',
|
||||||
|
titreFoncier: 'Titre Foncier',
|
||||||
|
numBatiment: 'N° Bâtiment',
|
||||||
|
numUniteLogement: 'N° Unité Logement',
|
||||||
|
ifu: 'IFU',
|
||||||
|
npi: 'NPI',
|
||||||
|
telProp: 'Tél. Propriétaire',
|
||||||
|
emailProp: 'Email Propriétaire',
|
||||||
|
nomProp: 'Nom Propriétaire',
|
||||||
|
prenomProp: 'Prénom Propriétaire',
|
||||||
|
raisonSociale: 'Raison Sociale',
|
||||||
|
adresseProp: 'Adresse Propriétaire',
|
||||||
|
telSc: 'Tél. Signataire',
|
||||||
|
emailSc: 'Email Signataire',
|
||||||
|
nomSc: 'Nom Signataire',
|
||||||
|
prenomSc: 'Prénom Signataire',
|
||||||
|
adresseSc: 'Adresse Signataire',
|
||||||
|
longitude: 'Longitude',
|
||||||
|
latitude: 'Latitude',
|
||||||
|
superficieParc: 'Superficie Parcelle (m²)',
|
||||||
|
superficieAuSolBat: 'Superficie Sol Bâtiment (m²)',
|
||||||
|
superficieAuSolLoue: 'Superficie Sol Louée (m²)',
|
||||||
|
superficieAuSolUlog: 'Superficie Sol Unité Log. (m²)',
|
||||||
|
batie: 'Bâtie',
|
||||||
|
exonere: 'Exonérée',
|
||||||
|
batimentExonere: 'Bâtiment Exonéré',
|
||||||
|
uniteLogementExonere: 'Unité Logement Exonérée',
|
||||||
|
valeurLocativeAdm: 'Valeur Locative Adm. (FCFA)',
|
||||||
|
valeurLocativeAdmMetreCarre: 'Val. Loc. Adm. / m² (FCFA)',
|
||||||
|
montantLoyerAnnuel: 'Montant Loyer Annuel (FCFA)',
|
||||||
|
tfuMetreCarre: 'TFU / m² (FCFA)',
|
||||||
|
tfuMinimum: 'TFU Minimum (FCFA)',
|
||||||
|
montantTaxe: 'Montant Taxe (FCFA)',
|
||||||
|
standingBat: 'Standing Bâtiment',
|
||||||
|
categorieBat: 'Catégorie Bâtiment',
|
||||||
|
categorieUsage: 'Usage',
|
||||||
|
nombrePiscine: 'Nombre Piscines',
|
||||||
|
nombreUlog: 'Nombre Unités Logement',
|
||||||
|
nombreBat: 'Nombre Bâtiments',
|
||||||
|
dateEnquete: 'Date Enquête',
|
||||||
|
tauxTfu: 'Taux TFU (%)',
|
||||||
|
valeurAdminParcelleNb: 'Val. Adm. Parcelle Non Bâtie (FCFA)',
|
||||||
|
valeurAdminParcelleNbMetreCarre: 'Val. Adm. Parc. NB / m² (FCFA)',
|
||||||
|
valeurBatiment: 'Valeur Bâtiment (FCFA)',
|
||||||
|
valeurParcelle: 'Valeur Parcelle (FCFA)',
|
||||||
|
// ── Champs réintégrés ─────────────────────────────
|
||||||
|
zoneRfu: 'Zone RFU',
|
||||||
|
valeurLocativeAdmTauxPropParc: 'Val. Loc. Adm. Taux Prop. Parc. (FCFA)',
|
||||||
|
valeurLocativeAdmSupReel: 'Val. Loc. Adm. Sup. Réelle (FCFA)',
|
||||||
|
superficieAuSolTauxPropParc: 'Superficie Sol Taux Prop. Parc. (m²)',
|
||||||
|
tfuCalculeTauxPropParc: 'TFU Calculé Taux Prop. Parc. (FCFA)',
|
||||||
|
tfuSuperficieAuSolReel: 'TFU Superficie Sol Réelle (FCFA)',
|
||||||
|
tfuPiscine: 'TFU Piscine (FCFA)',
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly CHAMPS_EXCLUS = new Set([
|
||||||
|
'id',
|
||||||
|
'externalKey',
|
||||||
|
'enqueteExternalKey',
|
||||||
|
'enqueteId',
|
||||||
|
'secteurId',
|
||||||
|
// ── tous les autres champs réintégrés ── supprimés
|
||||||
|
]);
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
numMenu = 2;
|
||||||
|
|
||||||
|
structureList: any[] = []; // Les noeuds de l'arbre
|
||||||
|
exerciceList: any[] = [];
|
||||||
|
|
||||||
|
structure: any = null;
|
||||||
|
exercice: any = null;
|
||||||
|
|
||||||
|
quartierSelected: any = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
nodes: NzTreeNodeOptions[] = []; // Les noeuds de l'arbre
|
||||||
|
|
||||||
|
arbreUtilisateurCourant: any[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private modalService: NzModalService,
|
||||||
|
private viewContainerRef: ViewContainerRef,
|
||||||
|
private excelExportService: ExcelExportService,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
if (this.user) {
|
||||||
|
this.crudService.getAll('structure/all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.structureList = data.object ? data.object : [];
|
||||||
|
});
|
||||||
|
this.crudService.getAll('secteur-decoupage/arbre/user-id/' + this.user?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.arbreUtilisateurCourant = data.object ? data.object : [];
|
||||||
|
if (this.arbreUtilisateurCourant && this.arbreUtilisateurCourant.length > 0) {
|
||||||
|
this.constructionArbreUtilisateurs();
|
||||||
|
}
|
||||||
|
this.message.success(`Chargement des découpages de l'utilisateur ${this.user?.nom} réussi`);
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
this.message.error(`Chargement des découpages de l'utilisateur ${this.user?.nom} échoué`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.crudService.getAll('exercice/all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.exerciceList = data.object ? data.object : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initFilterForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaire ────────────────────────────────────────
|
||||||
|
constructionArbreUtilisateurs() {
|
||||||
|
const data: any[] = this.arbreUtilisateurCourant;
|
||||||
|
// Grouper par département
|
||||||
|
const deptMap = new Map<number, any>();
|
||||||
|
|
||||||
|
data.forEach(item => {
|
||||||
|
if (!deptMap.has(item.departementId)) {
|
||||||
|
deptMap.set(item.departementId, {
|
||||||
|
...item,
|
||||||
|
communes: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const dept = deptMap.get(item.departementId);
|
||||||
|
|
||||||
|
if (!dept.communes.has(item.communeId)) {
|
||||||
|
dept.communes.set(item.communeId, {
|
||||||
|
...item,
|
||||||
|
arrondissements: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const commune = dept.communes.get(item.communeId);
|
||||||
|
|
||||||
|
if (!commune.arrondissements.has(item.arrondissementId)) {
|
||||||
|
commune.arrondissements.set(item.arrondissementId, {
|
||||||
|
...item,
|
||||||
|
quartiers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const arr = commune.arrondissements.get(item.arrondissementId);
|
||||||
|
arr.quartiers.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construire les noeuds
|
||||||
|
this.nodes = Array.from(deptMap.values()).map(dept => ({
|
||||||
|
title: `${dept.departementCode} — ${dept.departementNom}`,
|
||||||
|
key: `dept-${dept.departementId}`,
|
||||||
|
icon: 'bank',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: dept.nbParcellesDepartement,
|
||||||
|
children: Array.from(dept.communes.values()).map((comm: any) => ({
|
||||||
|
title: `${comm.communeCode} — ${comm.communeNom}`,
|
||||||
|
key: `comm-${comm.communeId}`,
|
||||||
|
icon: 'home',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: comm.nbParcellesCommune,
|
||||||
|
children: Array.from(comm.arrondissements.values()).map((arr: any) => ({
|
||||||
|
title: arr.arrondissementNom,
|
||||||
|
key: `arr-${arr.arrondissementId}`,
|
||||||
|
icon: 'apartment',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: arr.nbParcellesArrondissement,
|
||||||
|
children: arr.quartiers.map((quart: any) => ({
|
||||||
|
title: quart.quartierNom,
|
||||||
|
key: `quart-${quart.quartierId}`,
|
||||||
|
icon: 'environment',
|
||||||
|
isLeaf: true,
|
||||||
|
nbParcelles: quart.nbParcellesQuartier,
|
||||||
|
quartier: quart
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(event: any) {
|
||||||
|
const node = event.node;
|
||||||
|
if (node.isLeaf && node.key.startsWith('quart-')) {
|
||||||
|
this.quartierSelected = node.origin.quartier;
|
||||||
|
console.log(' this.quartierSelected ==>', this.quartierSelected);
|
||||||
|
this.message.create('success', `Quartier ${this.quartierSelected.quartierNom} sélectionné.`);
|
||||||
|
// votre logique de sélection...
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBadgeColor(niveau: string): string {
|
||||||
|
const colors: any = {
|
||||||
|
'dept': '#204e10',
|
||||||
|
'comm': '#10b981',
|
||||||
|
'arr': '#f59e0b',
|
||||||
|
'quart': '#ef6972'
|
||||||
|
};
|
||||||
|
return colors[niveau] ?? '#6b7280';
|
||||||
|
}
|
||||||
|
|
||||||
|
getNiveau(key: string): string {
|
||||||
|
if (key.startsWith('dept')) return 'dept';
|
||||||
|
if (key.startsWith('comm')) return 'comm';
|
||||||
|
if (key.startsWith('arr')) return 'arr';
|
||||||
|
if (key.startsWith('quart')) return 'quart';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init formulaire de filtre ─────────────────────────
|
||||||
|
initFilterForm(): void {
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
nup: [null],
|
||||||
|
titreFoncier: [null],
|
||||||
|
ilot: [null],
|
||||||
|
parcelle: [null],
|
||||||
|
nomProp: [null],
|
||||||
|
prenomProp: [null],
|
||||||
|
raisonSociale: [null],
|
||||||
|
ifu: [null],
|
||||||
|
npi: [null],
|
||||||
|
nomQuartierVillage: [null],
|
||||||
|
annee: [null],
|
||||||
|
natureImpot: [null],
|
||||||
|
batie: [null],
|
||||||
|
exonere: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
compareFn = (o1: any, o2: any) => (o1 && o2 ? o1.id === o2.id : o1 === o2);
|
||||||
|
|
||||||
|
changeExercice(value: any): void {
|
||||||
|
this.exercice = value;
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStructure(value: any): void {
|
||||||
|
console.log(' this.structure ', this.structure );
|
||||||
|
this.structure = value;
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chargement ────────────────────────────────────────
|
||||||
|
charger(): void {
|
||||||
|
if (!this.quartierSelected || !this.exercice || !this.structure) return;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const url = `donnees-impositions-tfu/all/by-exercice-id/by-structure-id/by-quartier-id/${this.exercice?.id}/${this.structure?.id}/${this.quartierSelected?.quartierId}`;
|
||||||
|
|
||||||
|
this.crudService.getAll(url).subscribe({
|
||||||
|
next: (data: any) => {
|
||||||
|
this.donnees = data?.object ?? [];
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.message.error('Erreur lors du chargement des impositions.');
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Filtre client-side ────────────────────────────────
|
||||||
|
get filteredList(): ImpositionPagedItem[] {
|
||||||
|
if (!this.filterApplied) return this.donnees;
|
||||||
|
|
||||||
|
const f = this.filterForm.value;
|
||||||
|
|
||||||
|
const match = (
|
||||||
|
filterVal: string | null | undefined,
|
||||||
|
itemVal: string | null | undefined
|
||||||
|
): boolean => {
|
||||||
|
if (!filterVal?.trim()) return true;
|
||||||
|
if (!itemVal?.trim()) return false;
|
||||||
|
return itemVal.toLowerCase().includes(filterVal.trim().toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchBool = (
|
||||||
|
filterVal: boolean | null | undefined,
|
||||||
|
itemVal: boolean | null | undefined
|
||||||
|
): boolean => {
|
||||||
|
if (filterVal === null || filterVal === undefined) return true;
|
||||||
|
return itemVal === filterVal;
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.donnees.filter(item =>
|
||||||
|
match(f.nup, item.nup) &&
|
||||||
|
match(f.titreFoncier, item.titreFoncier) &&
|
||||||
|
match(f.ilot, item.ilot) &&
|
||||||
|
match(f.parcelle, item.parcelle) &&
|
||||||
|
(
|
||||||
|
match(f.nomProp, item.nomProp) ||
|
||||||
|
match(f.nomProp, item.raisonSociale)
|
||||||
|
) &&
|
||||||
|
match(f.prenomProp, item.prenomProp) &&
|
||||||
|
match(f.raisonSociale, item.raisonSociale) &&
|
||||||
|
match(f.ifu, item.ifu) &&
|
||||||
|
match(f.npi, item.npi) &&
|
||||||
|
match(f.nomQuartierVillage, item.nomQuartierVillage) &&
|
||||||
|
match(f.natureImpot, item.natureImpot) &&
|
||||||
|
(!f.annee || item.annee?.toString() === f.annee?.toString()) &&
|
||||||
|
matchBool(f.batie, item.batie) &&
|
||||||
|
matchBool(f.exonere, item.exonere)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageCourante(): ImpositionPagedItem[] {
|
||||||
|
const debut = this.pageNo * this.pageSize;
|
||||||
|
return this.filteredList?.slice(debut, debut + this.pageSize) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalElements(): number { return this.filteredList.length; }
|
||||||
|
|
||||||
|
get totalPages(): number {
|
||||||
|
return this.pageSize > 0 ? Math.ceil(this.totalElements / this.pageSize) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageChange(page: number): void { this.pageNo = page - 1; }
|
||||||
|
|
||||||
|
onPageSizeChange(size: number): void {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appliquerFiltre(): void {
|
||||||
|
this.filterApplied = true;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinitialiserFiltre(): void {
|
||||||
|
this.filterForm?.reset();
|
||||||
|
this.filterApplied = false;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFiltre(): void { this.filterVisible = !this.filterVisible; }
|
||||||
|
|
||||||
|
toggleRow(id: number | undefined): void {
|
||||||
|
if (id == null) return;
|
||||||
|
this.expandedIds.has(id) ? this.expandedIds.delete(id) : this.expandedIds.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded(id: number | undefined): boolean {
|
||||||
|
return id != null && this.expandedIds.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatMontant(val: number | null | undefined): string {
|
||||||
|
if (val == null) return '—';
|
||||||
|
return new Intl.NumberFormat('fr-FR').format(val) + ' FCFA';
|
||||||
|
}
|
||||||
|
|
||||||
|
private nettoyerLigneExport(item: ImpositionPagedItem): { [label: string]: any } {
|
||||||
|
const ligne: { [label: string]: any } = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(this.EXPORT_LABELS)) {
|
||||||
|
if (this.CHAMPS_EXCLUS.has(key)) continue;
|
||||||
|
|
||||||
|
const label = this.EXPORT_LABELS[key];
|
||||||
|
|
||||||
|
// ── Cas spécial : zoneRfu → on n'exporte que le nom ──
|
||||||
|
if (key === 'zoneRfu') {
|
||||||
|
ligne[label] = (item.zoneRfu?.nom) || '—';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = (item as any)[key];
|
||||||
|
|
||||||
|
ligne[label] =
|
||||||
|
typeof val === 'boolean' ? (val ? 'OUI' : 'NON') :
|
||||||
|
val == null ? '—' : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ligne;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportPageCourante(): void {
|
||||||
|
if (!this.filteredList.length) {
|
||||||
|
this.message.warning('Aucune donnée à exporter.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = this.filteredList.map(item => this.nettoyerLigneExport(item));
|
||||||
|
this.excelExportService.exportAsExcelFile(data, 'impositions', 'Impositions');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,413 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<h6 class="card-title" style="font-size: 16px!important;text-transform: none;">
|
||||||
|
Liste des parcelles <strong class="text-primary"> - (quartier sélectionné :
|
||||||
|
{{ quartierSelected ? quartierSelected.quartierNom : '-' }}) </strong>
|
||||||
|
</h6>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- ══ ARBRE ══════════════════════════════════════ -->
|
||||||
|
<div class="col-md-3" style="padding-right:0;">
|
||||||
|
<div class="arbre-container">
|
||||||
|
|
||||||
|
<div class="arbre-title">
|
||||||
|
<span nz-icon nzType="apartment" nzTheme="outline"></span>
|
||||||
|
Divisions administratives
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-tree [nzData]="nodes" nzShowLine nzShowIcon [nzTreeTemplate]="nzTreeTemplate"
|
||||||
|
(nzClick)="onNodeClick($event)" (nzExpandChange)="onNodeClick($event)">
|
||||||
|
</nz-tree>
|
||||||
|
|
||||||
|
<ng-template #nzTreeTemplate let-node>
|
||||||
|
<div class="tree-node-row">
|
||||||
|
<span class="tree-node-icon">
|
||||||
|
<span nz-icon
|
||||||
|
[nzType]="node.isLeaf ? 'environment' : (node.isExpanded ? 'folder-open' : 'folder')"
|
||||||
|
[style.color]="getBadgeColor(getNiveau(node.key))" nzTheme="outline">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="tree-node-title" [class.tree-node-leaf]="node.isLeaf"
|
||||||
|
[class.tree-node-selected]="node.isLeaf && quartierSelected?.quartierId === node.origin?.quartier?.quartierId">
|
||||||
|
{{ node.title }}
|
||||||
|
</span>
|
||||||
|
<span class="tree-node-badge"
|
||||||
|
[style.background]="getBadgeColor(getNiveau(node.key))">
|
||||||
|
{{ node.origin?.nbParcelles | number:'1.0-0':'fr' }} p.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div *ngIf="nodes.length === 0" class="no-data">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:28px;"></span>
|
||||||
|
<span>Aucun département trouvé</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- fin arbre -->
|
||||||
|
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<div class="formulaire p-4"
|
||||||
|
style="width: 100%; border-radius: 5px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.08);">
|
||||||
|
<div>
|
||||||
|
<h2 style="font-size: 18px;margin-bottom: -10px;">Liste des parcelles
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<span style="background-color: #313131;font-size: 3px;margin-top:5px;">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<p style="font-size: 12px; color: #636363; margin-bottom: 30px;margin-top:5px;">
|
||||||
|
Cette interface affiche toutes les informations sur les parcelles
|
||||||
|
enregistrées
|
||||||
|
(identification, références foncières).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-container">
|
||||||
|
|
||||||
|
<!-- ── En-tête ── -->
|
||||||
|
<div class="pl-header">
|
||||||
|
<div class="pl-header-left">
|
||||||
|
<span nz-icon nzType="home" nzTheme="outline"
|
||||||
|
style="font-size:18px;color:#1f8653;"></span>
|
||||||
|
<div>
|
||||||
|
<div class="pl-title">Parcelles du quartier</div>
|
||||||
|
<div class="pl-count">{{ totalElements }} parcelle(s) au total</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-header-right">
|
||||||
|
<button class="pl-btn-filter mr-1" (click)="exportPageCourante()">
|
||||||
|
<span style="margin-top: -5px;" nz-icon nzType="file"
|
||||||
|
nzTheme="outline"></span>
|
||||||
|
Exporter en excel
|
||||||
|
<span class="pl-filter-badge" *ngIf="filterApplied">•</span>
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-filter"
|
||||||
|
[class.pl-btn-filter-active]="filterVisible || filterApplied"
|
||||||
|
(click)="toggleFiltre()">
|
||||||
|
<span style="margin-top: -5px;" nz-icon nzType="filter"
|
||||||
|
nzTheme="outline"></span>
|
||||||
|
Filtres
|
||||||
|
<span class="pl-filter-badge" *ngIf="filterApplied">•</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Panneau de filtres ── -->
|
||||||
|
<div class="pl-filter-panel" [class.pl-filter-panel-open]="filterVisible">
|
||||||
|
<form [formGroup]="filterForm">
|
||||||
|
<div class="pl-filter-grid">
|
||||||
|
|
||||||
|
<!-- Q / I / P -->
|
||||||
|
<!--<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Q (Quartier)</label>
|
||||||
|
<input class="pl-filter-input" formControlName="q"
|
||||||
|
placeholder="ex: 1120">
|
||||||
|
</div>-->
|
||||||
|
<div class="pl-filter-field"
|
||||||
|
style="display: grid;grid-template-columns: 1fr 1fr;">
|
||||||
|
<div style="width: 90px;">
|
||||||
|
<label class="pl-filter-label">I (Îlot)</label>
|
||||||
|
<input class="pl-filter-input" style="width: 90px;"
|
||||||
|
formControlName="i" placeholder="ex: 101">
|
||||||
|
</div>
|
||||||
|
<div style="width: 90px;">
|
||||||
|
<label class="pl-filter-label">P (Parcelle)</label>
|
||||||
|
<input class="pl-filter-input" style="width: 90px;"
|
||||||
|
formControlName="p" placeholder="ex: A">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NUP / TF -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUP</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nup"
|
||||||
|
placeholder="ex: NUP-001">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">N° Titre Foncier</label>
|
||||||
|
<input class="pl-filter-input" formControlName="numTitreFoncier"
|
||||||
|
placeholder="ex: TF-2023">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="proprietaireNom"
|
||||||
|
placeholder="ex: CODO">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Prénom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="proprietairePrenom"
|
||||||
|
placeholder="ex: MICHEL">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">IFU / NC propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="proprietaireIfu"
|
||||||
|
placeholder="ex: 011056">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Type domaine</label>
|
||||||
|
<select class="pl-filter-input" formControlName="typeDomaineId"
|
||||||
|
(change)="getNatureDomaineList($event)">
|
||||||
|
<option *ngFor="let item of typeDomaineList" value="{{ item.id }}">
|
||||||
|
{{ item.libelle }} </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Domaine -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nature domaine</label>
|
||||||
|
<select class="pl-filter-input" formControlName="natureDomaineId">
|
||||||
|
<option *ngFor="let item of natureDomaineFilteredList"
|
||||||
|
value="{{ item.id }}"> {{ item.libelle }} </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-filter-actions">
|
||||||
|
<button class="pl-btn-reset" type="button" (click)="reinitialiserFiltre()">
|
||||||
|
<span nz-icon nzType="redo" nzTheme="outline"></span>
|
||||||
|
Réinitialiser
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-apply" type="button" (click)="appliquerFiltre()">
|
||||||
|
<span nz-icon nzType="search" nzTheme="outline"></span>
|
||||||
|
Appliquer
|
||||||
|
<span *ngIf="filterApplied" style="margin-left:4px;">
|
||||||
|
({{ filteredList.length }} résultat(s))
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Loading ── -->
|
||||||
|
<div class="pl-loading" *ngIf="loading">
|
||||||
|
<nz-spin nzSimple></nz-spin>
|
||||||
|
<span>Chargement des parcelles…</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Liste ── -->
|
||||||
|
<div *ngIf="!loading">
|
||||||
|
|
||||||
|
<!-- Aucune donnée -->
|
||||||
|
<div class="pl-empty" *ngIf="filteredList.length === 0">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:32px;"></span>
|
||||||
|
<p>Aucune parcelle trouvée</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cards -->
|
||||||
|
<div class="pl-card" *ngFor="let item of pageCourante">
|
||||||
|
|
||||||
|
<!-- Ligne principale -->
|
||||||
|
<div class="pl-card-main">
|
||||||
|
|
||||||
|
<!-- Colonne : Identification -->
|
||||||
|
<div class="pl-col pl-col-identity">
|
||||||
|
<div class="pl-badges">
|
||||||
|
<span class="pl-badge pl-badge-qip">
|
||||||
|
Q{{ item.q }} . {{ item.i }} . {{ item.p }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-enquete"
|
||||||
|
*ngIf="item.enqueteCouranteId">
|
||||||
|
<span nz-icon nzType="file-search" nzTheme="outline"></span>
|
||||||
|
Enquêtée
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-no-enquete"
|
||||||
|
*ngIf="!item.enqueteCouranteId">
|
||||||
|
Non enquêtée
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-nup">
|
||||||
|
{{ item.nup || item.nupProvisoire || '— NUP non défini' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.numTitreFoncier">
|
||||||
|
<span nz-icon nzType="file-protect" nzTheme="outline"></span>
|
||||||
|
TF : {{ item.numTitreFoncier }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line">
|
||||||
|
<span nz-icon nzType="environment" nzTheme="outline"></span>
|
||||||
|
{{ item.quartierNom || '—' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Colonne : Domaine -->
|
||||||
|
<div class="pl-col pl-col-domaine">
|
||||||
|
<div class="pl-domaine-type">{{ item.typeDomaineLibelle || '—' }}</div>
|
||||||
|
<div class="pl-domaine-nature">{{ item.natureDomaineLibelle || '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.superficie">
|
||||||
|
<span nz-icon nzType="expand" nzTheme="outline"></span>
|
||||||
|
{{ item.superficie | number:'1.0-2':'fr' }} m²
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Colonne : Propriétaire -->
|
||||||
|
<div class="pl-col pl-col-prop">
|
||||||
|
<div class="pl-prop-name">
|
||||||
|
{{ item.proprietaireRaisonSociale
|
||||||
|
|| ((item.proprietaireNom || '') + ' ' + (item.proprietairePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.proprietaireIfu">
|
||||||
|
IFU : {{ item.proprietaireIfu.trim() }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.proprietaireNpi">
|
||||||
|
NPI : {{ item.proprietaireNpi }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.numEntreeParcelle">
|
||||||
|
<span nz-icon nzType="number" nzTheme="outline"></span>
|
||||||
|
Entrée : {{ item.numEntreeParcelle.trim() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Colonne : Action -->
|
||||||
|
<div class="pl-col pl-col-action">
|
||||||
|
<button class="pl-btn-detail" (click)="toggleRow(item.id)">
|
||||||
|
<span nz-icon [nzType]="isExpanded(item.id) ? 'up' : 'down'"></span>
|
||||||
|
{{ isExpanded(item.id) ? 'Réduire' : 'Détails' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin pl-card-main -->
|
||||||
|
|
||||||
|
<!-- Détails expandés -->
|
||||||
|
<div class="pl-card-detail" *ngIf="isExpanded(item.id)">
|
||||||
|
<div class="pl-detail-grid">
|
||||||
|
|
||||||
|
<!--<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">ID Parcelle</span>
|
||||||
|
<span class="pl-detail-val">{{ item.id }}</span>
|
||||||
|
</div>-->
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Q . I . P</span>
|
||||||
|
<span class="pl-detail-val">{{ item.q }} . {{ item.i }} .
|
||||||
|
{{ item.p }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUP</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nup || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUP Provisoire</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nupProvisoire || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">N° Titre Foncier</span>
|
||||||
|
<span class="pl-detail-val">{{ item.numTitreFoncier || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficie || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Quartier</span>
|
||||||
|
<span class="pl-detail-val">{{ item.quartierCode }} —
|
||||||
|
{{ item.quartierNom }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Rue</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.rueNom ? (item.rueNumero ? 'N°' + item.rueNumero + ' ' : '') + item.rueNom : '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">N° Entrée</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.numEntreeParcelle?.trim() || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Type Domaine</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.typeDomaineLibelle || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Nature Domaine</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.natureDomaineLibelle || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Longitude / Latitude</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.longitude ? (item.longitude + ' / ' + item.latitude) : '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Propriétaire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.proprietaireRaisonSociale
|
||||||
|
|| ((item.proprietaireNom || '') + ' ' + (item.proprietairePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">IFU</span>
|
||||||
|
<span
|
||||||
|
class="pl-detail-val">{{ item.proprietaireIfu?.trim() || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NPI</span>
|
||||||
|
<span class="pl-detail-val">{{ item.proprietaireNpi || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item" *ngIf="item.observation">
|
||||||
|
<span class="pl-detail-label">Observation</span>
|
||||||
|
<span class="pl-detail-val">{{ item.observation }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<button class="btn-action btn-action-person"
|
||||||
|
(click)="afficherDetail(item.id)">
|
||||||
|
<i class="mdi mdi-arrow-right"></i> Plus de détails
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin cards -->
|
||||||
|
|
||||||
|
<!-- ── Pagination ── -->
|
||||||
|
<div class="pl-pagination" *ngIf="totalElements > pageSize">
|
||||||
|
<span class="pl-pagination-info">
|
||||||
|
Page {{ pageNo + 1 }} sur {{ totalPages }} — {{ totalElements }} parcelle(s)
|
||||||
|
</span>
|
||||||
|
<nz-pagination [nzPageIndex]="pageNo + 1" [nzTotal]="totalElements"
|
||||||
|
[nzPageSize]="pageSize" [nzShowSizeChanger]="true"
|
||||||
|
[nzPageSizeOptions]="[10, 20, 50, 100]"
|
||||||
|
(nzPageIndexChange)="onPageChange($event)"
|
||||||
|
(nzPageSizeChange)="onPageSizeChange($event)">
|
||||||
|
</nz-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,517 @@
|
|||||||
|
import { Component, SimpleChanges, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { ExcelExportService } from 'src/app/excel-export.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
// parcelle-paged.model.ts
|
||||||
|
export interface ParcellePagedItem {
|
||||||
|
id?: number;
|
||||||
|
q?: string;
|
||||||
|
i?: string;
|
||||||
|
p?: string;
|
||||||
|
nup?: string;
|
||||||
|
nupProvisoire?: string;
|
||||||
|
numTitreFoncier?: string;
|
||||||
|
longitude?: string;
|
||||||
|
latitude?: string;
|
||||||
|
altitude?: string;
|
||||||
|
superficie?: number;
|
||||||
|
observation?: string;
|
||||||
|
situationGeographique?: string;
|
||||||
|
numEntreeParcelle?: string;
|
||||||
|
quartierId?: number;
|
||||||
|
quartierCode?: string;
|
||||||
|
quartierNom?: string;
|
||||||
|
natureDomaineId?: string;
|
||||||
|
natureDomaineLibelle?: string;
|
||||||
|
typeDomaineId?: string;
|
||||||
|
typeDomaineLibelle?: string;
|
||||||
|
rueId?: number;
|
||||||
|
rueNumero?: string;
|
||||||
|
rueNom?: string;
|
||||||
|
proprietaireId?: number;
|
||||||
|
proprietaireIfu?: string;
|
||||||
|
proprietaireNpi?: string;
|
||||||
|
proprietaireNom?: string;
|
||||||
|
proprietairePrenom?: string;
|
||||||
|
proprietaireRaisonSociale?: string;
|
||||||
|
enqueteCouranteId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list-parcelle-consultation',
|
||||||
|
templateUrl: './list-parcelle-consultation.component.html',
|
||||||
|
styleUrls: ['./list-parcelle-consultation.component.css']
|
||||||
|
})
|
||||||
|
export class ListParcelleConsultationComponent {
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
numMenu = 2;
|
||||||
|
|
||||||
|
nodes: NzTreeNodeOptions[] = []; // Les noeuds de l'arbre
|
||||||
|
|
||||||
|
arbreUtilisateurCourant: any[] = [];
|
||||||
|
quartierSelected: any = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
// ── Données ───────────────────────────────────────────
|
||||||
|
parcelleList: ParcellePagedItem[] = [];
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
// ── Pagination ────────────────────────────────────────
|
||||||
|
// ── Pagination client-side ────────────────────────────
|
||||||
|
pageNo = 0;
|
||||||
|
pageSize = 10;
|
||||||
|
|
||||||
|
// ── Données brutes complètes (chargées une seule fois) ──
|
||||||
|
donnees: ParcellePagedItem[] = [];
|
||||||
|
|
||||||
|
// ── Filtre ────────────────────────────────────────────
|
||||||
|
filterForm!: FormGroup;
|
||||||
|
filterVisible = false;
|
||||||
|
filterApplied = false;
|
||||||
|
|
||||||
|
// ── Ligne expandée ────────────────────────────────────
|
||||||
|
expandedIds = new Set<number>();
|
||||||
|
|
||||||
|
natureDomaineFilteredList: any[] = [];
|
||||||
|
natureDomaineList: any[] = [];
|
||||||
|
typeDomaineList: any[] = [];
|
||||||
|
|
||||||
|
// ── Mapping des labels français pour l'export ─────────────
|
||||||
|
private readonly EXPORT_LABELS: { [key: string]: string } = {
|
||||||
|
|
||||||
|
// ── Identifiant ───────────────────────────────────────
|
||||||
|
id: 'ID Parcelle',
|
||||||
|
|
||||||
|
// ── Identification parcellaire ────────────────────────
|
||||||
|
q: 'Q (Quartier)',
|
||||||
|
i: 'I (Îlot)',
|
||||||
|
p: 'P (Parcelle)',
|
||||||
|
nup: 'NUP',
|
||||||
|
nupProvisoire: 'NUP Provisoire',
|
||||||
|
numTitreFoncier: 'N° Titre Foncier',
|
||||||
|
|
||||||
|
// ── GPS ───────────────────────────────────────────────
|
||||||
|
longitude: 'Longitude',
|
||||||
|
latitude: 'Latitude',
|
||||||
|
altitude: 'Altitude (m)',
|
||||||
|
|
||||||
|
// ── Caractéristiques ──────────────────────────────────
|
||||||
|
superficie: 'Superficie (m²)',
|
||||||
|
observation: 'Observation',
|
||||||
|
situationGeographique: 'Situation Géographique',
|
||||||
|
numEntreeParcelle: 'N° Entrée Parcelle',
|
||||||
|
|
||||||
|
// ── Quartier ──────────────────────────────────────────
|
||||||
|
quartierId: 'ID Quartier',
|
||||||
|
quartierCode: 'Code Quartier',
|
||||||
|
quartierNom: 'Quartier',
|
||||||
|
|
||||||
|
// ── Nature domaine ────────────────────────────────────
|
||||||
|
natureDomaineId: 'ID Nature Domaine',
|
||||||
|
natureDomaineLibelle: 'Nature Domaine',
|
||||||
|
|
||||||
|
// ── Type domaine ──────────────────────────────────────
|
||||||
|
typeDomaineId: 'ID Type Domaine',
|
||||||
|
typeDomaineLibelle: 'Type Domaine',
|
||||||
|
|
||||||
|
// ── Rue ───────────────────────────────────────────────
|
||||||
|
rueId: 'ID Rue',
|
||||||
|
rueNumero: 'N° Rue',
|
||||||
|
rueNom: 'Nom Rue',
|
||||||
|
|
||||||
|
// ── Propriétaire ──────────────────────────────────────
|
||||||
|
proprietaireId: 'ID Propriétaire',
|
||||||
|
proprietaireIfu: 'IFU Propriétaire',
|
||||||
|
proprietaireNpi: 'NPI Propriétaire',
|
||||||
|
proprietaireNom: 'Nom Propriétaire',
|
||||||
|
proprietairePrenom: 'Prénom Propriétaire',
|
||||||
|
proprietaireRaisonSociale: 'Raison Sociale Propriétaire',
|
||||||
|
|
||||||
|
// ── Enquête ───────────────────────────────────────────
|
||||||
|
enqueteCouranteId: 'ID Enquête Courante',
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Champs à exclure de l'export ──────────────────────────
|
||||||
|
private readonly CHAMPS_EXCLUS = new Set([
|
||||||
|
'id',
|
||||||
|
'quartierId',
|
||||||
|
'natureDomaineId',
|
||||||
|
'typeDomaineId',
|
||||||
|
'rueId',
|
||||||
|
'proprietaireId',
|
||||||
|
'enqueteCouranteId',
|
||||||
|
]);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private modalService: NzModalService,
|
||||||
|
private viewContainerRef: ViewContainerRef,
|
||||||
|
private excelExportService: ExcelExportService,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
if (this.user) {
|
||||||
|
this.crudService.getAll('secteur-decoupage/arbre/user-id/' + this.user?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.arbreUtilisateurCourant = data.object ? data.object : [];
|
||||||
|
if (this.arbreUtilisateurCourant && this.arbreUtilisateurCourant.length > 0) {
|
||||||
|
this.constructionArbreUtilisateurs();
|
||||||
|
}
|
||||||
|
this.message.success(`Chargement des découpages de l'utilisateur ${this.user?.nom} réussi`);
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
this.message.error(`Chargement des découpages de l'utilisateur ${this.user?.nom} échoué`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initFilterForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes['quartierId'] && this.quartierSelected?.quartierId) {
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.reinitialiserFiltre();
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getNatureDomaineList(value: any): void {
|
||||||
|
//console.log('value type domaine', value);
|
||||||
|
this.natureDomaineFilteredList = [];
|
||||||
|
if (value) {
|
||||||
|
this.natureDomaineFilteredList = this.natureDomaineList.filter((el) => el.typeDomaine?.id == value.target.value);
|
||||||
|
} else {
|
||||||
|
this.natureDomaineFilteredList = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init formulaire de filtre ─────────────────────────
|
||||||
|
initFilterForm(): void {
|
||||||
|
this.crudService.getAll('nature-domaine/all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.natureDomaineList = data.object ? data.object : [];
|
||||||
|
});
|
||||||
|
this.crudService.getAll('type-domaine/all').subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.typeDomaineList = data.object ? data.object : [];
|
||||||
|
});
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
q: [null],
|
||||||
|
i: [null],
|
||||||
|
p: [null],
|
||||||
|
nup: [null],
|
||||||
|
numTitreFoncier: [null],
|
||||||
|
proprietaireNom: [null],
|
||||||
|
proprietairePrenom: [null],
|
||||||
|
proprietaireIfu: [null],
|
||||||
|
natureDomaineId: [null],
|
||||||
|
typeDomaineId: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chargement paginé ─────────────────────────────────
|
||||||
|
charger(): void {
|
||||||
|
if (!this.quartierSelected) return;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const url = `parcelle/all/by-quartier-id/${this.quartierSelected?.quartierId}`;
|
||||||
|
|
||||||
|
this.crudService.getAll(url).subscribe({
|
||||||
|
next: (data: any) => {
|
||||||
|
this.donnees = data?.object ?? [];
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.message.error('Erreur lors du chargement des parcelles.');
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get filteredList(): ParcellePagedItem[] {
|
||||||
|
if (!this.filterApplied) {
|
||||||
|
return this.donnees;
|
||||||
|
}
|
||||||
|
|
||||||
|
const f = this.filterForm.value;
|
||||||
|
|
||||||
|
// Fonction de match plus stricte et claire
|
||||||
|
const match = (filterValue: string | null | undefined, itemValue: string | null | undefined): boolean => {
|
||||||
|
// Si le filtre est vide → on accepte (on ne filtre pas sur ce critère)
|
||||||
|
if (!filterValue?.trim()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si le champ de la donnée est vide/null → on refuse (le filtre demande quelque chose)
|
||||||
|
if (!itemValue?.trim()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recherche insensible à la casse + trim
|
||||||
|
return itemValue.toLowerCase().includes(filterValue.trim().toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.donnees.filter(p => {
|
||||||
|
return (
|
||||||
|
match(f.q, p.q) &&
|
||||||
|
match(f.i, p.i) &&
|
||||||
|
match(f.p, p.p) &&
|
||||||
|
match(f.nup, p.nup) &&
|
||||||
|
match(f.numTitreFoncier, p.numTitreFoncier) &&
|
||||||
|
// Propriétaire : nom OU raison sociale
|
||||||
|
(match(f.proprietaireNom, p.proprietaireNom) ||
|
||||||
|
match(f.proprietaireNom, p.proprietaireRaisonSociale)) &&
|
||||||
|
match(f.proprietairePrenom, p.proprietairePrenom) &&
|
||||||
|
match(f.proprietaireIfu, p.proprietaireIfu) &&
|
||||||
|
match(f.natureDomaineId, p.natureDomaineId?.toString()) && // ← souvent number → toString()
|
||||||
|
match(f.typeDomaineId, p.typeDomaineId?.toString())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Éléments de la page courante ─────────────────────────
|
||||||
|
get pageCourante(): ParcellePagedItem[] {
|
||||||
|
const debut = this.pageNo * this.pageSize;
|
||||||
|
const fin = debut + this.pageSize;
|
||||||
|
return this.filteredList.slice(debut, fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Total des éléments filtrés ───────────────────────────
|
||||||
|
get totalElements(): number {
|
||||||
|
return this.filteredList.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Total des éléments filtrés ───────────────────────────
|
||||||
|
get totalPages(): number {
|
||||||
|
const count = this.filteredList?.length ?? 0;
|
||||||
|
const size = this.pageSize ?? 10; // valeur par défaut si pageSize n'est pas défini
|
||||||
|
|
||||||
|
if (size <= 0 || count === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.ceil(count / size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pagination ───────────────────────────────────────────
|
||||||
|
onPageChange(page: number): void {
|
||||||
|
this.pageNo = page - 1;
|
||||||
|
// ← pas d'appel API, on retranche simplement la page
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageSizeChange(size: number): void {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.pageNo = 0;
|
||||||
|
// ← pas d'appel API
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Filtre : reset la page à 0 à chaque application ──────
|
||||||
|
appliquerFiltre(): void {
|
||||||
|
this.filterApplied = true;
|
||||||
|
this.pageNo = 0; // ← retour page 1 après filtre
|
||||||
|
}
|
||||||
|
|
||||||
|
reinitialiserFiltre(): void {
|
||||||
|
this.filterForm?.reset();
|
||||||
|
this.filterApplied = false;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFiltre(): void {
|
||||||
|
this.filterVisible = !this.filterVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Expand ligne ──────────────────────────────────────
|
||||||
|
toggleRow(id: number | undefined): void {
|
||||||
|
if (id == null) return;
|
||||||
|
if (this.expandedIds.has(id)) {
|
||||||
|
this.expandedIds.delete(id);
|
||||||
|
} else {
|
||||||
|
this.expandedIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded(id: number | undefined): boolean {
|
||||||
|
return id != null && this.expandedIds.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaire ────────────────────────────────────────
|
||||||
|
formatMontant(val: number | null | undefined): string {
|
||||||
|
if (val == null) return '—';
|
||||||
|
return new Intl.NumberFormat('fr-FR').format(val) + ' FCFA';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructionArbreUtilisateurs() {
|
||||||
|
const data: any[] = this.arbreUtilisateurCourant;
|
||||||
|
// Grouper par département
|
||||||
|
const deptMap = new Map<number, any>();
|
||||||
|
|
||||||
|
data.forEach(item => {
|
||||||
|
if (!deptMap.has(item.departementId)) {
|
||||||
|
deptMap.set(item.departementId, {
|
||||||
|
...item,
|
||||||
|
communes: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const dept = deptMap.get(item.departementId);
|
||||||
|
|
||||||
|
if (!dept.communes.has(item.communeId)) {
|
||||||
|
dept.communes.set(item.communeId, {
|
||||||
|
...item,
|
||||||
|
arrondissements: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const commune = dept.communes.get(item.communeId);
|
||||||
|
|
||||||
|
if (!commune.arrondissements.has(item.arrondissementId)) {
|
||||||
|
commune.arrondissements.set(item.arrondissementId, {
|
||||||
|
...item,
|
||||||
|
quartiers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const arr = commune.arrondissements.get(item.arrondissementId);
|
||||||
|
arr.quartiers.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construire les noeuds
|
||||||
|
this.nodes = Array.from(deptMap.values()).map(dept => ({
|
||||||
|
title: `${dept.departementCode} — ${dept.departementNom}`,
|
||||||
|
key: `dept-${dept.departementId}`,
|
||||||
|
icon: 'bank',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: dept.nbParcellesDepartement,
|
||||||
|
children: Array.from(dept.communes.values()).map((comm: any) => ({
|
||||||
|
title: `${comm.communeCode} — ${comm.communeNom}`,
|
||||||
|
key: `comm-${comm.communeId}`,
|
||||||
|
icon: 'home',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: comm.nbParcellesCommune,
|
||||||
|
children: Array.from(comm.arrondissements.values()).map((arr: any) => ({
|
||||||
|
title: arr.arrondissementNom,
|
||||||
|
key: `arr-${arr.arrondissementId}`,
|
||||||
|
icon: 'apartment',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: arr.nbParcellesArrondissement,
|
||||||
|
children: arr.quartiers.map((quart: any) => ({
|
||||||
|
title: quart.quartierNom,
|
||||||
|
key: `quart-${quart.quartierId}`,
|
||||||
|
icon: 'environment',
|
||||||
|
isLeaf: true,
|
||||||
|
nbParcelles: quart.nbParcellesQuartier,
|
||||||
|
quartier: quart
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(event: any) {
|
||||||
|
const node = event.node;
|
||||||
|
if (node.isLeaf && node.key.startsWith('quart-')) {
|
||||||
|
this.quartierSelected = node.origin.quartier;
|
||||||
|
console.log(' this.quartierSelected ==>', this.quartierSelected);
|
||||||
|
this.message.create('success', `Quartier ${this.quartierSelected.quartierNom} sélectionné.`);
|
||||||
|
// votre logique de sélection...
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBadgeColor(niveau: string): string {
|
||||||
|
const colors: any = {
|
||||||
|
'dept': '#204e10',
|
||||||
|
'comm': '#10b981',
|
||||||
|
'arr': '#f59e0b',
|
||||||
|
'quart': '#ef6972'
|
||||||
|
};
|
||||||
|
return colors[niveau] ?? '#6b7280';
|
||||||
|
}
|
||||||
|
|
||||||
|
getNiveau(key: string): string {
|
||||||
|
if (key.startsWith('dept')) return 'dept';
|
||||||
|
if (key.startsWith('comm')) return 'comm';
|
||||||
|
if (key.startsWith('arr')) return 'arr';
|
||||||
|
if (key.startsWith('quart')) return 'quart';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Nettoyage et formatage d'une ligne ───────────────────
|
||||||
|
private nettoyerLigneExport(item: any): { [label: string]: any } {
|
||||||
|
const ligne: { [label: string]: any } = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(this.EXPORT_LABELS)) {
|
||||||
|
if (this.CHAMPS_EXCLUS.has(key)) continue;
|
||||||
|
|
||||||
|
const label = this.EXPORT_LABELS[key];
|
||||||
|
const val = item[key];
|
||||||
|
|
||||||
|
// Booléens → OUI / NON
|
||||||
|
if (typeof val === 'boolean') {
|
||||||
|
ligne[label] = val ? 'OUI' : 'NON';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null / undefined → tiret
|
||||||
|
if (val === null || val === undefined) {
|
||||||
|
ligne[label] = '—';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ligne[label] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ligne;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Export de la page courante ────────────────────────────
|
||||||
|
exportPageCourante(): void {
|
||||||
|
if (!this.filteredList || this.filteredList.length === 0) {
|
||||||
|
this.message.warning('Aucune donnée à exporter.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = this.filteredList.map(item => this.nettoyerLigneExport(item));
|
||||||
|
this.excelExportService.exportAsExcelFile(
|
||||||
|
data,
|
||||||
|
`parcelles`,
|
||||||
|
'Parcelles'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
afficherDetail(id: any): void {
|
||||||
|
this.router.navigate(['/core/consultation/detail-parcelle/'+ id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,420 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<h6 class="card-title" style="font-size: 16px!important;text-transform: none;">
|
||||||
|
Liste des unités de logements <strong class="text-primary"> - (quartier sélectionné :
|
||||||
|
{{ quartierSelected ? quartierSelected.quartierNom : '-' }}) </strong>
|
||||||
|
</h6>
|
||||||
|
<nz-divider></nz-divider>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- ══ ARBRE ══════════════════════════════════════ -->
|
||||||
|
<div class="col-md-3" style="padding-right:0;">
|
||||||
|
<div class="arbre-container">
|
||||||
|
|
||||||
|
<div class="arbre-title">
|
||||||
|
<span nz-icon nzType="apartment" nzTheme="outline"></span>
|
||||||
|
Divisions administratives
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nz-tree [nzData]="nodes" nzShowLine nzShowIcon [nzTreeTemplate]="nzTreeTemplate"
|
||||||
|
(nzClick)="onNodeClick($event)" (nzExpandChange)="onNodeClick($event)">
|
||||||
|
</nz-tree>
|
||||||
|
|
||||||
|
<ng-template #nzTreeTemplate let-node>
|
||||||
|
<div class="tree-node-row">
|
||||||
|
<span class="tree-node-icon">
|
||||||
|
<span nz-icon
|
||||||
|
[nzType]="node.isLeaf ? 'environment' : (node.isExpanded ? 'folder-open' : 'folder')"
|
||||||
|
[style.color]="getBadgeColor(getNiveau(node.key))" nzTheme="outline">
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="tree-node-title" [class.tree-node-leaf]="node.isLeaf"
|
||||||
|
[class.tree-node-selected]="node.isLeaf && quartierSelected?.quartierId === node.origin?.quartier?.quartierId">
|
||||||
|
{{ node.title }}
|
||||||
|
</span>
|
||||||
|
<!--<span class="tree-node-badge"
|
||||||
|
[style.background]="getBadgeColor(getNiveau(node.key))">
|
||||||
|
{{ node.origin?.nbParcelles | number:'1.0-0':'fr' }} p.
|
||||||
|
</span>-->
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div *ngIf="nodes.length === 0" class="no-data">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:28px;"></span>
|
||||||
|
<span>Aucun département trouvé</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- fin arbre -->
|
||||||
|
|
||||||
|
<div class="col-md-9">
|
||||||
|
|
||||||
|
<div class="formulaire p-4"
|
||||||
|
style="width: 100%; border-radius: 5px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.08);">
|
||||||
|
<div>
|
||||||
|
<h2 style="font-size: 18px;margin-bottom: -10px;">Liste des unités de logement
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<span style="background-color: #313131;font-size: 3px;margin-top:5px;">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<p style="font-size: 12px; color: #636363; margin-bottom: 30px;margin-top:5px;">
|
||||||
|
Cette interface affiche toutes les informations sur les unités de logement
|
||||||
|
enregistrées (identification, références foncières, catégorie, usage).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-container">
|
||||||
|
|
||||||
|
<!-- ── En-tête ── -->
|
||||||
|
<div class="pl-header">
|
||||||
|
<div class="pl-header-left">
|
||||||
|
<span nz-icon nzType="apartment" nzTheme="outline"
|
||||||
|
style="font-size:18px;color:#1f8653;"></span>
|
||||||
|
<div>
|
||||||
|
<div class="pl-title">Unités de logement du quartier</div>
|
||||||
|
<div class="pl-count">{{ totalElements }} unité(s) au total</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-header-right">
|
||||||
|
<button class="pl-btn-filter mr-1" (click)="exportPageCourante()">
|
||||||
|
<span nz-icon nzType="file-excel" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Exporter en Excel
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-filter"
|
||||||
|
[class.pl-btn-filter-active]="filterVisible || filterApplied"
|
||||||
|
(click)="toggleFiltre()">
|
||||||
|
<span nz-icon nzType="filter" nzTheme="outline"
|
||||||
|
style="margin-top:-5px;"></span>
|
||||||
|
Filtres
|
||||||
|
<span class="pl-filter-badge" *ngIf="filterApplied">•</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Panneau filtres ── -->
|
||||||
|
<div class="pl-filter-panel" [class.pl-filter-panel-open]="filterVisible">
|
||||||
|
<form [formGroup]="filterForm">
|
||||||
|
<div class="pl-filter-grid">
|
||||||
|
|
||||||
|
<!-- NUL / Code / Étage -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUL</label>
|
||||||
|
<input class="pl-filter-input" formControlName="nul"
|
||||||
|
placeholder="ex: UL-001">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Code unité</label>
|
||||||
|
<input class="pl-filter-input" formControlName="code"
|
||||||
|
placeholder="ex: CODE-001">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Numéro d'étage</label>
|
||||||
|
<input class="pl-filter-input" formControlName="numeroEtage"
|
||||||
|
placeholder="ex: 2">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NUB Bâtiment -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">NUB Bâtiment</label>
|
||||||
|
<input class="pl-filter-input" formControlName="batimentNub"
|
||||||
|
placeholder="ex: B01">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Nom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="personneNom"
|
||||||
|
placeholder="ex: CODO">
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Prénom propriétaire</label>
|
||||||
|
<input class="pl-filter-input" formControlName="personnePrenom"
|
||||||
|
placeholder="ex: MICHEL">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Catégorie & Usage -->
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Catégorie bâtiment</label>
|
||||||
|
<select class="pl-filter-input" formControlName="categorieBatimentId">
|
||||||
|
<option value="">-- Toutes --</option>
|
||||||
|
<option *ngFor="let cat of categorieBatimentList" [value]="cat.id">
|
||||||
|
{{ cat.code }} — {{ cat.standing }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="pl-filter-field">
|
||||||
|
<label class="pl-filter-label">Usage</label>
|
||||||
|
<select class="pl-filter-input" formControlName="usageId">
|
||||||
|
<option value="">-- Tous --</option>
|
||||||
|
<option *ngFor="let usage of usageList" [value]="usage.id">
|
||||||
|
{{ usage.nom }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-filter-actions">
|
||||||
|
<button class="pl-btn-reset" type="button" (click)="reinitialiserFiltre()">
|
||||||
|
<span nz-icon nzType="redo" nzTheme="outline"></span>
|
||||||
|
Réinitialiser
|
||||||
|
</button>
|
||||||
|
<button class="pl-btn-apply" type="button" (click)="appliquerFiltre()">
|
||||||
|
<span nz-icon nzType="search" nzTheme="outline"></span>
|
||||||
|
Appliquer
|
||||||
|
<span *ngIf="filterApplied" style="margin-left:4px;">
|
||||||
|
({{ filteredList.length }} résultat(s))
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Loading ── -->
|
||||||
|
<div class="pl-loading" *ngIf="loading">
|
||||||
|
<nz-spin nzSimple></nz-spin>
|
||||||
|
<span>Chargement des unités de logement…</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Liste ── -->
|
||||||
|
<div *ngIf="!loading">
|
||||||
|
|
||||||
|
<div class="pl-empty" *ngIf="filteredList.length === 0">
|
||||||
|
<span nz-icon nzType="inbox" nzTheme="outline" style="font-size:32px;"></span>
|
||||||
|
<p>Aucune unité de logement trouvée</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cards -->
|
||||||
|
<div class="pl-card" *ngFor="let item of pageCourante">
|
||||||
|
|
||||||
|
<div class="pl-card-main">
|
||||||
|
|
||||||
|
<!-- Identification -->
|
||||||
|
<div class="pl-col pl-col-identity">
|
||||||
|
<div class="pl-badges">
|
||||||
|
<span class="pl-badge pl-badge-qip">
|
||||||
|
NUL : {{ item.nul || '—' }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge" *ngIf="item.categorieBatimentCode"
|
||||||
|
style="background:#e0f2fe;color:#0369a1;">
|
||||||
|
{{ item.categorieBatimentCode }}
|
||||||
|
{{ item.categorieBatimentStanding ? '— ' + item.categorieBatimentStanding : '' }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-enquete"
|
||||||
|
*ngIf="item.enqueteUniteLogementCourantId">
|
||||||
|
<span nz-icon nzType="file-search" nzTheme="outline"></span>
|
||||||
|
Enquêtée
|
||||||
|
</span>
|
||||||
|
<span class="pl-badge pl-badge-no-enquete"
|
||||||
|
*ngIf="!item.enqueteUniteLogementCourantId">
|
||||||
|
Non enquêtée
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-nup">
|
||||||
|
Code : {{ item.code || '—' }}
|
||||||
|
— Étage : {{ item.numeroEtage || '0' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.batimentNub">
|
||||||
|
<span nz-icon nzType="bank" nzTheme="outline"></span>
|
||||||
|
Bâtiment NUB : {{ item.batimentNub }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-info-line" *ngIf="item.dateConstruction">
|
||||||
|
<span nz-icon nzType="calendar" nzTheme="outline"></span>
|
||||||
|
Construit le : {{ item.dateConstruction | date:'dd/MM/yyyy' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Usage & Surfaces -->
|
||||||
|
<div class="pl-col pl-col-domaine">
|
||||||
|
<div class="pl-domaine-type">{{ item.usageNom || '—' }}</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.superficieAuSol">
|
||||||
|
<span nz-icon nzType="expand" nzTheme="outline"></span>
|
||||||
|
{{ item.superficieAuSol | number:'1.0-2':'fr' }} m² au sol
|
||||||
|
</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.superficieLouee">
|
||||||
|
<span nz-icon nzType="shrink" nzTheme="outline"></span>
|
||||||
|
{{ item.superficieLouee | number:'1.0-2':'fr' }} m² loués
|
||||||
|
</div>
|
||||||
|
<div class="pl-superficie" *ngIf="item.nombrePiscine">
|
||||||
|
🏊 {{ item.nombrePiscine }} piscine(s)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propriétaire -->
|
||||||
|
<div class="pl-col pl-col-prop">
|
||||||
|
<div class="pl-prop-name">
|
||||||
|
{{ item.personneRaisonSociale
|
||||||
|
|| ((item.personneNom || '') + ' ' + (item.personnePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.valeurUniteLogementEstime">
|
||||||
|
Val. estimée : {{ formatMontant(item.valeurUniteLogementEstime) }}
|
||||||
|
</div>
|
||||||
|
<div class="pl-prop-sub" *ngIf="item.montantMensuelLocation">
|
||||||
|
Loyer mensuel : {{ formatMontant(item.montantMensuelLocation) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
|
<div class="pl-col pl-col-action">
|
||||||
|
<button class="pl-btn-detail" (click)="toggleRow(item.id)">
|
||||||
|
<span nz-icon [nzType]="isExpanded(item.id) ? 'up' : 'down'"></span>
|
||||||
|
{{ isExpanded(item.id) ? 'Réduire' : 'Détails' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Détails expandés -->
|
||||||
|
<div class="pl-card-detail" *ngIf="isExpanded(item.id)">
|
||||||
|
<div class="pl-detail-grid">
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUL</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nul || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Code</span>
|
||||||
|
<span class="pl-detail-val">{{ item.code || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Numéro d'étage</span>
|
||||||
|
<span class="pl-detail-val">{{ item.numeroEtage || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">NUB Bâtiment</span>
|
||||||
|
<span class="pl-detail-val">{{ item.batimentNub || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Date construction</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ (item.dateConstruction | date:'dd/MM/yyyy') || '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Catégorie / Standing</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.categorieBatimentCode || '—' }}
|
||||||
|
{{ item.categorieBatimentStanding ? '— ' + item.categorieBatimentStanding : '' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Usage</span>
|
||||||
|
<span class="pl-detail-val">{{ item.usageNom || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie au sol (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficieAuSol || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Superficie louée (m²)</span>
|
||||||
|
<span class="pl-detail-val">{{ item.superficieLouee || '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Nombre piscines</span>
|
||||||
|
<span class="pl-detail-val">{{ item.nombrePiscine ?? '—' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Propriétaire</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ item.personneRaisonSociale
|
||||||
|
|| ((item.personneNom || '') + ' ' + (item.personnePrenom || ''))
|
||||||
|
|| '—' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer mensuel</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.montantMensuelLocation) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel déclaré</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.montantLocatifAnnuelDeclare) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel calculé</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.montantLocatifAnnuelCalcule) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Loyer annuel estimé</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.montantLocatifAnnuelEstime) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. estimée U.L.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurUniteLogementEstime) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. réelle U.L.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurUniteLogementReel) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-label">Val. calculée U.L.</span>
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
{{ formatMontant(item.valeurUniteLogementCalcule) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item" *ngIf="item.observation">
|
||||||
|
<span class="pl-detail-label">Observation</span>
|
||||||
|
<span class="pl-detail-val">{{ item.observation }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pl-detail-item">
|
||||||
|
<span class="pl-detail-val">
|
||||||
|
<button class="btn-action btn-action-person"
|
||||||
|
(click)="afficherDetail(item.id)">
|
||||||
|
<i class="mdi mdi-arrow-right"></i> Plus de détails
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- fin cards -->
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div class="pl-pagination" *ngIf="totalElements > pageSize">
|
||||||
|
<span class="pl-pagination-info">
|
||||||
|
Page {{ pageNo + 1 }} sur {{ totalPages }}
|
||||||
|
— {{ totalElements }} unité(s) de logement
|
||||||
|
</span>
|
||||||
|
<nz-pagination [nzPageIndex]="pageNo + 1" [nzTotal]="totalElements"
|
||||||
|
[nzPageSize]="pageSize" [nzShowSizeChanger]="true"
|
||||||
|
[nzPageSizeOptions]="[10, 20, 50, 100]"
|
||||||
|
(nzPageIndexChange)="onPageChange($event)"
|
||||||
|
(nzPageSizeChange)="onPageSizeChange($event)">
|
||||||
|
</nz-pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
import { Component, SimpleChanges, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { ExcelExportService } from 'src/app/excel-export.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
|
||||||
|
export interface UniteLogementPagedItem {
|
||||||
|
id?: number;
|
||||||
|
nul?: string;
|
||||||
|
numeroEtage?: string;
|
||||||
|
code?: string;
|
||||||
|
batimentId?: number;
|
||||||
|
batimentNub?: string;
|
||||||
|
superficieAuSol?: number;
|
||||||
|
superficieLouee?: number;
|
||||||
|
observation?: string;
|
||||||
|
dateConstruction?: string;
|
||||||
|
personneId?: number;
|
||||||
|
personneNom?: string;
|
||||||
|
personnePrenom?: string;
|
||||||
|
personneRaisonSociale?: string;
|
||||||
|
enqueteUniteLogementCourantId?: number;
|
||||||
|
categorieBatimentId?: number;
|
||||||
|
categorieBatimentCode?: string;
|
||||||
|
categorieBatimentStanding?: string;
|
||||||
|
montantMensuelLocation?: number;
|
||||||
|
montantLocatifAnnuelDeclare?: number;
|
||||||
|
montantLocatifAnnuelCalcule?: number;
|
||||||
|
montantLocatifAnnuelEstime?: number;
|
||||||
|
valeurUniteLogementEstime?: number;
|
||||||
|
valeurUniteLogementReel?: number;
|
||||||
|
valeurUniteLogementCalcule?: number;
|
||||||
|
nombrePiscine?: number;
|
||||||
|
usageId?: number;
|
||||||
|
usageNom?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list-unite-logement-consultation',
|
||||||
|
templateUrl: './list-unite-logement-consultation.component.html',
|
||||||
|
styleUrls: ['./list-unite-logement-consultation.component.css']
|
||||||
|
})
|
||||||
|
export class ListUniteLogementConsultationComponent {
|
||||||
|
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
numMenu = 2;
|
||||||
|
|
||||||
|
nodes: NzTreeNodeOptions[] = []; // Les noeuds de l'arbre
|
||||||
|
|
||||||
|
arbreUtilisateurCourant: any[] = [];
|
||||||
|
quartierSelected: any = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
|
||||||
|
// ── Données ───────────────────────────────────────────
|
||||||
|
donnees: UniteLogementPagedItem[] = [];
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
// ── Pagination client-side ────────────────────────────
|
||||||
|
pageNo = 0;
|
||||||
|
pageSize = 10;
|
||||||
|
|
||||||
|
// ── Filtre ────────────────────────────────────────────
|
||||||
|
filterForm!: FormGroup;
|
||||||
|
filterVisible = false;
|
||||||
|
filterApplied = false;
|
||||||
|
|
||||||
|
// ── Ligne expandée ────────────────────────────────────
|
||||||
|
expandedIds = new Set<number>();
|
||||||
|
|
||||||
|
// ── Référentiels ──────────────────────────────────────
|
||||||
|
usageList: any[] = [];
|
||||||
|
categorieBatimentList: any[] = [];
|
||||||
|
|
||||||
|
// ── EXPORT LABELS ─────────────────────────────────────
|
||||||
|
private readonly EXPORT_LABELS: { [key: string]: string } = {
|
||||||
|
id: 'ID Unité Logement',
|
||||||
|
nul: 'NUL',
|
||||||
|
numeroEtage: 'Numéro Étage',
|
||||||
|
code: 'Code Unité Logement',
|
||||||
|
batimentId: 'ID Bâtiment',
|
||||||
|
batimentNub: 'NUB Bâtiment',
|
||||||
|
superficieAuSol: 'Superficie au Sol (m²)',
|
||||||
|
superficieLouee: 'Superficie Louée (m²)',
|
||||||
|
observation: 'Observation',
|
||||||
|
dateConstruction: 'Date Construction',
|
||||||
|
personneId: 'ID Propriétaire',
|
||||||
|
personneNom: 'Nom Propriétaire',
|
||||||
|
personnePrenom: 'Prénom Propriétaire',
|
||||||
|
personneRaisonSociale: 'Raison Sociale Propriétaire',
|
||||||
|
enqueteUniteLogementCourantId: 'ID Enquête Courante',
|
||||||
|
categorieBatimentId: 'ID Catégorie Bâtiment',
|
||||||
|
categorieBatimentCode: 'Code Catégorie Bâtiment',
|
||||||
|
categorieBatimentStanding: 'Standing Bâtiment',
|
||||||
|
montantMensuelLocation: 'Loyer Mensuel (FCFA)',
|
||||||
|
montantLocatifAnnuelDeclare: 'Loyer Annuel Déclaré (FCFA)',
|
||||||
|
montantLocatifAnnuelCalcule: 'Loyer Annuel Calculé (FCFA)',
|
||||||
|
montantLocatifAnnuelEstime: 'Loyer Annuel Estimé (FCFA)',
|
||||||
|
valeurUniteLogementEstime: 'Valeur Estimée U.L. (FCFA)',
|
||||||
|
valeurUniteLogementReel: 'Valeur Réelle U.L. (FCFA)',
|
||||||
|
valeurUniteLogementCalcule: 'Valeur Calculée U.L. (FCFA)',
|
||||||
|
nombrePiscine: 'Nombre Piscines',
|
||||||
|
usageId: 'ID Usage',
|
||||||
|
usageNom: 'Usage',
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly CHAMPS_EXCLUS = new Set([
|
||||||
|
'id', 'batimentId', 'personneId',
|
||||||
|
'enqueteUniteLogementCourantId', 'categorieBatimentId', 'usageId',
|
||||||
|
]);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private modalService: NzModalService,
|
||||||
|
private viewContainerRef: ViewContainerRef,
|
||||||
|
private excelExportService: ExcelExportService,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
if (this.user) {
|
||||||
|
this.crudService.getAll('secteur-decoupage/arbre/user-id/' + this.user?.id).subscribe(
|
||||||
|
(data: any) => {
|
||||||
|
this.arbreUtilisateurCourant = data.object ? data.object : [];
|
||||||
|
if (this.arbreUtilisateurCourant && this.arbreUtilisateurCourant.length > 0) {
|
||||||
|
this.constructionArbreUtilisateurs();
|
||||||
|
}
|
||||||
|
this.message.success(`Chargement des découpages de l'utilisateur ${this.user?.nom} réussi`);
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
this.message.error(`Chargement des découpages de l'utilisateur ${this.user?.nom} échoué`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initFilterForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes['quartierId'] && this.quartierSelected?.quartierId) {
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.reinitialiserFiltre();
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaire ────────────────────────────────────────
|
||||||
|
constructionArbreUtilisateurs() {
|
||||||
|
const data: any[] = this.arbreUtilisateurCourant;
|
||||||
|
// Grouper par département
|
||||||
|
const deptMap = new Map<number, any>();
|
||||||
|
|
||||||
|
data.forEach(item => {
|
||||||
|
if (!deptMap.has(item.departementId)) {
|
||||||
|
deptMap.set(item.departementId, {
|
||||||
|
...item,
|
||||||
|
communes: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const dept = deptMap.get(item.departementId);
|
||||||
|
|
||||||
|
if (!dept.communes.has(item.communeId)) {
|
||||||
|
dept.communes.set(item.communeId, {
|
||||||
|
...item,
|
||||||
|
arrondissements: new Map<number, any>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const commune = dept.communes.get(item.communeId);
|
||||||
|
|
||||||
|
if (!commune.arrondissements.has(item.arrondissementId)) {
|
||||||
|
commune.arrondissements.set(item.arrondissementId, {
|
||||||
|
...item,
|
||||||
|
quartiers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const arr = commune.arrondissements.get(item.arrondissementId);
|
||||||
|
arr.quartiers.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Construire les noeuds
|
||||||
|
this.nodes = Array.from(deptMap.values()).map(dept => ({
|
||||||
|
title: `${dept.departementCode} — ${dept.departementNom}`,
|
||||||
|
key: `dept-${dept.departementId}`,
|
||||||
|
icon: 'bank',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: dept.nbParcellesDepartement,
|
||||||
|
children: Array.from(dept.communes.values()).map((comm: any) => ({
|
||||||
|
title: `${comm.communeCode} — ${comm.communeNom}`,
|
||||||
|
key: `comm-${comm.communeId}`,
|
||||||
|
icon: 'home',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: comm.nbParcellesCommune,
|
||||||
|
children: Array.from(comm.arrondissements.values()).map((arr: any) => ({
|
||||||
|
title: arr.arrondissementNom,
|
||||||
|
key: `arr-${arr.arrondissementId}`,
|
||||||
|
icon: 'apartment',
|
||||||
|
isLeaf: false,
|
||||||
|
expanded: false,
|
||||||
|
nbParcelles: arr.nbParcellesArrondissement,
|
||||||
|
children: arr.quartiers.map((quart: any) => ({
|
||||||
|
title: quart.quartierNom,
|
||||||
|
key: `quart-${quart.quartierId}`,
|
||||||
|
icon: 'environment',
|
||||||
|
isLeaf: true,
|
||||||
|
nbParcelles: quart.nbParcellesQuartier,
|
||||||
|
quartier: quart
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(event: any) {
|
||||||
|
const node = event.node;
|
||||||
|
if (node.isLeaf && node.key.startsWith('quart-')) {
|
||||||
|
this.quartierSelected = node.origin.quartier;
|
||||||
|
console.log(' this.quartierSelected ==>', this.quartierSelected);
|
||||||
|
this.message.create('success', `Quartier ${this.quartierSelected.quartierNom} sélectionné.`);
|
||||||
|
// votre logique de sélection...
|
||||||
|
this.charger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBadgeColor(niveau: string): string {
|
||||||
|
const colors: any = {
|
||||||
|
'dept': '#204e10',
|
||||||
|
'comm': '#10b981',
|
||||||
|
'arr': '#f59e0b',
|
||||||
|
'quart': '#ef6972'
|
||||||
|
};
|
||||||
|
return colors[niveau] ?? '#6b7280';
|
||||||
|
}
|
||||||
|
|
||||||
|
getNiveau(key: string): string {
|
||||||
|
if (key.startsWith('dept')) return 'dept';
|
||||||
|
if (key.startsWith('comm')) return 'comm';
|
||||||
|
if (key.startsWith('arr')) return 'arr';
|
||||||
|
if (key.startsWith('quart')) return 'quart';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Init formulaire de filtre ─────────────────────────
|
||||||
|
initFilterForm(): void {
|
||||||
|
this.crudService.getAll('usage/all').subscribe((data: any) => {
|
||||||
|
this.usageList = data.object ?? [];
|
||||||
|
});
|
||||||
|
this.crudService.getAll('categorie-batiment/all').subscribe((data: any) => {
|
||||||
|
this.categorieBatimentList = data.object ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.filterForm = this.fb.group({
|
||||||
|
nul: [null],
|
||||||
|
code: [null],
|
||||||
|
numeroEtage: [null],
|
||||||
|
batimentNub: [null],
|
||||||
|
personneNom: [null],
|
||||||
|
personnePrenom: [null],
|
||||||
|
personneRaisonSociale: [null],
|
||||||
|
categorieBatimentId: [null],
|
||||||
|
usageId: [null],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chargement ────────────────────────────────────────
|
||||||
|
charger(): void {
|
||||||
|
if (!this.quartierSelected) return;
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
const url = `unite-logement/all/by-quartier-id/${this.quartierSelected?.quartierId}`;
|
||||||
|
|
||||||
|
this.crudService.getAll(url).subscribe({
|
||||||
|
next: (data: any) => {
|
||||||
|
this.donnees = data?.object ?? [];
|
||||||
|
this.pageNo = 0;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.message.error('Erreur lors du chargement des unités de logement.');
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Filtre client-side ────────────────────────────────
|
||||||
|
get filteredList(): UniteLogementPagedItem[] {
|
||||||
|
if (!this.filterApplied) return this.donnees;
|
||||||
|
|
||||||
|
const f = this.filterForm.value;
|
||||||
|
|
||||||
|
const match = (
|
||||||
|
filterVal: string | null | undefined,
|
||||||
|
itemVal: string | null | undefined
|
||||||
|
): boolean => {
|
||||||
|
if (!filterVal?.trim()) return true;
|
||||||
|
if (!itemVal?.trim()) return false;
|
||||||
|
return itemVal.toLowerCase().includes(filterVal.trim().toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.donnees.filter(u =>
|
||||||
|
match(f.nul, u.nul) &&
|
||||||
|
match(f.code, u.code) &&
|
||||||
|
match(f.numeroEtage, u.numeroEtage) &&
|
||||||
|
match(f.batimentNub, u.batimentNub) &&
|
||||||
|
(
|
||||||
|
match(f.personneNom, u.personneNom) ||
|
||||||
|
match(f.personneNom, u.personneRaisonSociale)
|
||||||
|
) &&
|
||||||
|
match(f.personnePrenom, u.personnePrenom) &&
|
||||||
|
(!f.categorieBatimentId || u.categorieBatimentId?.toString() === f.categorieBatimentId) &&
|
||||||
|
(!f.usageId || u.usageId?.toString() === f.usageId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageCourante(): UniteLogementPagedItem[] {
|
||||||
|
const debut = this.pageNo * this.pageSize;
|
||||||
|
return this.filteredList.slice(debut, debut + this.pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalElements(): number { return this.filteredList.length; }
|
||||||
|
|
||||||
|
get totalPages(): number {
|
||||||
|
return this.pageSize > 0 ? Math.ceil(this.totalElements / this.pageSize) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageChange(page: number): void { this.pageNo = page - 1; }
|
||||||
|
|
||||||
|
onPageSizeChange(size: number): void {
|
||||||
|
this.pageSize = size;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
appliquerFiltre(): void {
|
||||||
|
this.filterApplied = true;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reinitialiserFiltre(): void {
|
||||||
|
this.filterForm?.reset();
|
||||||
|
this.filterApplied = false;
|
||||||
|
this.pageNo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFiltre(): void { this.filterVisible = !this.filterVisible; }
|
||||||
|
|
||||||
|
toggleRow(id: number | undefined): void {
|
||||||
|
if (id == null) return;
|
||||||
|
this.expandedIds.has(id) ? this.expandedIds.delete(id) : this.expandedIds.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded(id: number | undefined): boolean {
|
||||||
|
return id != null && this.expandedIds.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatMontant(val: number | null | undefined): string {
|
||||||
|
if (val == null) return '—';
|
||||||
|
return new Intl.NumberFormat('fr-FR').format(val) + ' FCFA';
|
||||||
|
}
|
||||||
|
|
||||||
|
private nettoyerLigneExport(item: any): { [label: string]: any } {
|
||||||
|
const ligne: { [label: string]: any } = {};
|
||||||
|
for (const key of Object.keys(this.EXPORT_LABELS)) {
|
||||||
|
if (this.CHAMPS_EXCLUS.has(key)) continue;
|
||||||
|
const val = item[key];
|
||||||
|
ligne[this.EXPORT_LABELS[key]] =
|
||||||
|
typeof val === 'boolean' ? (val ? 'OUI' : 'NON') :
|
||||||
|
val == null ? '—' : val;
|
||||||
|
}
|
||||||
|
return ligne;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportPageCourante(): void {
|
||||||
|
if (!this.filteredList.length) {
|
||||||
|
this.message.warning('Aucune donnée à exporter.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = this.filteredList.map(item => this.nettoyerLigneExport(item));
|
||||||
|
this.excelExportService.exportAsExcelFile(data, 'unites-logement', 'Unités Logement');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
afficherDetail(id: any): void {
|
||||||
|
this.router.navigate(['/core/consultation/detail-unite-logement/' + id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,606 @@
|
|||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
RESET NZ-CARD BODY
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.kpi-card .ant-card-body,
|
||||||
|
.stat-banner-card .ant-card-body,
|
||||||
|
.chart-card .ant-card-body,
|
||||||
|
.stats-table-card .ant-card-body,
|
||||||
|
.alert-card .ant-card-body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
DASHBOARD CONTAINER
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 24px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
KPI CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.kpi-cards-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(26, 88, 144, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 24px;
|
||||||
|
gap: 18px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-content::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-icon {
|
||||||
|
width: 56px; height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-icon [nz-icon],
|
||||||
|
.kpi-icon span[nz-icon] {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-details { flex: 1; }
|
||||||
|
|
||||||
|
.kpi-value {
|
||||||
|
font-size: 34px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-value-small {
|
||||||
|
font-size: 18px !important;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-unit {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.kpi-card-blue .kpi-content::before { background: linear-gradient(90deg, #1a5890, #0e3660); }*/
|
||||||
|
.kpi-card-blue .kpi-icon [nz-icon] { color: #1a5890; }
|
||||||
|
.kpi-card-blue .kpi-value { color: #1a5890; }
|
||||||
|
|
||||||
|
/*.kpi-card-green .kpi-content::before { background: linear-gradient(90deg, #10b981, #059669); }*/
|
||||||
|
.kpi-card-green .kpi-icon [nz-icon] { color: #10b981; }
|
||||||
|
.kpi-card-green .kpi-value { color: #10b981; }
|
||||||
|
.kpi-card-green .kpi-value-small { color: #10b981; }
|
||||||
|
|
||||||
|
/*.kpi-card-purple .kpi-content::before { background: linear-gradient(90deg, #1a5890, #6d28d9); }*/
|
||||||
|
.kpi-card-purple .kpi-icon [nz-icon] { color: #1a5890; }
|
||||||
|
.kpi-card-purple .kpi-value { color: #1a5890; }
|
||||||
|
|
||||||
|
/*.kpi-card-red .kpi-content::before { background: linear-gradient(90deg, #ef4444, #dc2626); }*/
|
||||||
|
.kpi-card-red .kpi-icon [nz-icon] { color: #ef4444; }
|
||||||
|
.kpi-card-red .kpi-value { color: #ef4444; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
STAT BANNER
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.stat-banner-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.10);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
transition: filter 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-item:hover { filter: brightness(0.96); }
|
||||||
|
|
||||||
|
.stat-banner-green { background: linear-gradient(135deg, #f0fdf4, #dcfce7); }
|
||||||
|
.stat-banner-blue { background: linear-gradient(135deg, #e8f1fb, #cce0f5); }
|
||||||
|
.stat-banner-purple { background: linear-gradient(135deg, #eef2fb, #d6e4f5); }
|
||||||
|
.stat-banner-orange { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
|
||||||
|
|
||||||
|
.stat-banner-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
width: 48px; height: 48px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-green .stat-banner-icon { color: #10b981; background: rgba(16, 185, 129, 0.12); }
|
||||||
|
.stat-banner-blue .stat-banner-icon { color: #1a5890; background: rgba(26, 88, 144, 0.12); }
|
||||||
|
.stat-banner-purple .stat-banner-icon { color: #1a5890; background: rgba(26, 88, 144, 0.10); }
|
||||||
|
.stat-banner-orange .stat-banner-icon { color: #f59e0b; background: rgba(245, 158, 11, 0.12); }
|
||||||
|
|
||||||
|
.stat-banner-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex; flex-direction: column; gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
color: #111827;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-unit {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b7280;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar {
|
||||||
|
width: 100%; height: 5px;
|
||||||
|
background: rgba(0,0,0,0.07);
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-bar-fill.green { background: #10b981; }
|
||||||
|
.stat-banner-bar-fill.blue { background: #1a5890; }
|
||||||
|
.stat-banner-bar-fill.purple { background: #1a5890; }
|
||||||
|
.stat-banner-bar-fill.orange { background: #f59e0b; }
|
||||||
|
|
||||||
|
.stat-banner-percent {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #9ca3af;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-banner-divider {
|
||||||
|
width: 1px;
|
||||||
|
/*background: rgba(0,0,0,0.07);*/
|
||||||
|
margin: 12px 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
PIPELINE STATUTS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.statuts-pipeline {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(26, 88, 144, 0.08);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-item {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-count {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-bar {
|
||||||
|
width: 100%; height: 4px;
|
||||||
|
background: rgba(0,0,0,0.08);
|
||||||
|
border-radius: 999px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-warning { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
|
||||||
|
.pipeline-warning .pipeline-count { color: #d97706; }
|
||||||
|
|
||||||
|
.pipeline-blue { background: linear-gradient(135deg, #e8f1fb, #cce0f5); }
|
||||||
|
.pipeline-blue .pipeline-count { color: #1a5890; }
|
||||||
|
|
||||||
|
.pipeline-cyan { background: linear-gradient(135deg, #ecfeff, #cffafe); }
|
||||||
|
.pipeline-cyan .pipeline-count { color: #0891b2; }
|
||||||
|
|
||||||
|
.pipeline-green { background: linear-gradient(135deg, #f0fdf4, #dcfce7); }
|
||||||
|
.pipeline-green .pipeline-count { color: #16a34a; }
|
||||||
|
|
||||||
|
.pipeline-red { background: linear-gradient(135deg, #fff1f2, #ffe4e6); }
|
||||||
|
.pipeline-red .pipeline-count { color: #dc2626; }
|
||||||
|
|
||||||
|
.pipeline-arrow {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #c5d9ef;
|
||||||
|
font-weight: 700;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-separator {
|
||||||
|
width: 2px; height: 50px;
|
||||||
|
background: #e0ecf8;
|
||||||
|
border-radius: 999px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ONGLETS THÉMATIQUES
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.thematique-tabs-section {
|
||||||
|
margin-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thematique-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
padding: 10px 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #6b7280;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 6px 6px 0 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab:hover {
|
||||||
|
color: #1a5890;
|
||||||
|
background: #e8f1fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ttab-active {
|
||||||
|
color: #1a5890 !important;
|
||||||
|
border-bottom-color: #1a5890 !important;
|
||||||
|
background: #e8f1fb !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thematique-content {
|
||||||
|
animation: fadeInUp 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
CHART CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.chart-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.08);
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a5890;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-title [nz-icon] {
|
||||||
|
color: #1a5890;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-content {
|
||||||
|
padding: 16px 20px;
|
||||||
|
min-height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: #f4f8fd;
|
||||||
|
border-top: 1px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-summary {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
font-size: 11px; color: #6b7280; font-weight: 500;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
font-size: 18px; font-weight: 700; color: #1a5890;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
STATS TABLE CARD
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.stats-table-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px rgba(26, 88, 144, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 2px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title {
|
||||||
|
font-size: 14px; font-weight: 700; color: #1a5890;
|
||||||
|
margin: 0; display: flex; align-items: center; gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-title [nz-icon] { color: #1a5890; font-size: 18px; }
|
||||||
|
|
||||||
|
.fonction-name { font-weight: 600; color: #1a5890; }
|
||||||
|
|
||||||
|
.ant-table { font-size: 13px; }
|
||||||
|
|
||||||
|
.ant-table thead > tr > th {
|
||||||
|
background: #e8f1fb !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: #1a5890 !important;
|
||||||
|
border-bottom: 2px solid #c5d9ef !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table tbody > tr:hover > td { background: #f4f8fd !important; }
|
||||||
|
.ant-table tbody > tr > td { padding: 12px 16px !important; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
MONTANT CELLS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.montant-cell { font-weight: 600; color: #1a5890; }
|
||||||
|
.montant-cell-green { font-weight: 600; color: #10b981; }
|
||||||
|
.montant-total { font-size: 14px; font-weight: 700; color: #0e3660; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
EVOLUTION BADGE
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.evol-badge {
|
||||||
|
display: inline-flex; align-items: center; gap: 3px;
|
||||||
|
padding: 3px 8px; border-radius: 999px;
|
||||||
|
font-size: 11px; font-weight: 700;
|
||||||
|
background: #f3f4f6; color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.evol-badge.evol-up { background: #dcfce7; color: #16a34a; }
|
||||||
|
.evol-badge.evol-zero { background: #f3f4f6; color: #9ca3af; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
SYNTHESE LIST
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.synthese-list {
|
||||||
|
padding: 12px 16px;
|
||||||
|
display: flex; flex-direction: column; gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.synthese-item {
|
||||||
|
display: flex; align-items: center; gap: 12px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: #f4f8fd;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-left: 3px solid #e0ecf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.synthese-icon {
|
||||||
|
width: 36px; height: 36px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 18px; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.synthese-icon.blue { background: rgba(26,88,144,0.10); color: #1a5890; }
|
||||||
|
.synthese-icon.green { background: rgba(16,185,129,0.10); color: #10b981; }
|
||||||
|
.synthese-icon.orange { background: rgba(245,158,11,0.10); color: #f59e0b; }
|
||||||
|
.synthese-icon.red { background: rgba(239,68,68,0.10); color: #ef4444; }
|
||||||
|
|
||||||
|
.synthese-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.synthese-label { font-size: 12px; font-weight: 500; color: #6b7280; }
|
||||||
|
.synthese-val { font-size: 14px; font-weight: 700; color: #1a5890; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ALERT CARDS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
.alert-card {
|
||||||
|
border-radius: 12px; border: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-content {
|
||||||
|
display: flex; align-items: flex-start;
|
||||||
|
gap: 16px; padding: 20px; border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-icon { font-size: 30px; flex-shrink: 0; margin-top: 2px; }
|
||||||
|
.alert-body { flex: 1; }
|
||||||
|
|
||||||
|
.alert-title {
|
||||||
|
font-size: 11px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-value {
|
||||||
|
font-size: 26px; font-weight: 700; line-height: 1.1; margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-desc { font-size: 12px; opacity: 0.72; }
|
||||||
|
|
||||||
|
.alert-warning { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
|
||||||
|
.alert-warning .alert-icon { color: #f59e0b; }
|
||||||
|
.alert-warning .alert-title { color: #92400e; }
|
||||||
|
.alert-warning .alert-value { color: #d97706; }
|
||||||
|
.alert-warning .alert-desc { color: #78350f; }
|
||||||
|
|
||||||
|
.alert-danger { background: linear-gradient(135deg, #fff1f2, #ffe4e6); }
|
||||||
|
.alert-danger .alert-icon { color: #ef4444; }
|
||||||
|
.alert-danger .alert-title { color: #991b1b; }
|
||||||
|
.alert-danger .alert-value { color: #dc2626; }
|
||||||
|
.alert-danger .alert-desc { color: #7f1d1d; }
|
||||||
|
|
||||||
|
.alert-success { background: linear-gradient(135deg, #f0fdf4, #dcfce7); }
|
||||||
|
.alert-success .alert-icon { color: #10b981; }
|
||||||
|
.alert-success .alert-title { color: #14532d; }
|
||||||
|
.alert-success .alert-value { color: #16a34a; }
|
||||||
|
.alert-success .alert-desc { color: #15803d; }
|
||||||
|
|
||||||
|
.alert-info { background: linear-gradient(135deg, #e8f1fb, #cce0f5); }
|
||||||
|
.alert-info .alert-icon { color: #1a5890; }
|
||||||
|
.alert-info .alert-title { color: #0e3660; }
|
||||||
|
.alert-info .alert-value { color: #1a5890; }
|
||||||
|
.alert-info .alert-desc { color: #2563eb; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
ANIMATIONS
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from { opacity: 0; transform: translateY(16px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card { animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
.chart-card { animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
.stats-table-card { animation: fadeInUp 0.45s ease-out both; }
|
||||||
|
|
||||||
|
.kpi-card:nth-child(1) { animation-delay: 0.05s; }
|
||||||
|
.kpi-card:nth-child(2) { animation-delay: 0.12s; }
|
||||||
|
.kpi-card:nth-child(3) { animation-delay: 0.19s; }
|
||||||
|
.kpi-card:nth-child(4) { animation-delay: 0.26s; }
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════════════
|
||||||
|
RESPONSIVE
|
||||||
|
══════════════════════════════════════════════════════════════ */
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.stat-banner-container { flex-wrap: wrap; }
|
||||||
|
.stat-banner-item { flex: 0 0 50%; min-width: 0; }
|
||||||
|
.stat-banner-divider { display: none; }
|
||||||
|
.statuts-pipeline { gap: 6px; }
|
||||||
|
.pipeline-item { min-width: 90px; padding: 10px 12px; }
|
||||||
|
.pipeline-count { font-size: 22px; }
|
||||||
|
.pipeline-arrow { display: none; }
|
||||||
|
.pipeline-separator { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.dashboard-container { padding: 12px; }
|
||||||
|
.stat-banner-container { flex-direction: column; }
|
||||||
|
.stat-banner-item { flex: 1 1 100%; }
|
||||||
|
.thematique-tabs { gap: 2px; }
|
||||||
|
.ttab { padding: 8px 10px; font-size: 12px; }
|
||||||
|
.kpi-content { padding: 14px 16px; }
|
||||||
|
.kpi-value { font-size: 26px; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,743 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<!-- ── KPIs principaux ── -->
|
||||||
|
<div class="kpi-cards-section">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner l'exercice fiscale">
|
||||||
|
<nz-option *ngFor="let item of getAnneeList()" [nzLabel]="item"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Exercice Fiscale <span
|
||||||
|
class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner la commune">
|
||||||
|
<nz-option *ngFor="let item of getCommuneList()" [nzLabel]="item"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Commune <span
|
||||||
|
class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear
|
||||||
|
nzPlaceHolder="Selectionner la direction / centre d'impôt">
|
||||||
|
<nz-option *ngFor="let item of getStructureList()" [nzLabel]="item"
|
||||||
|
[nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Direction / centre d'impôt <span
|
||||||
|
class="text-danger"> *</span> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-red" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="home" nzTheme="outline"></span>
|
||||||
|
</div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">
|
||||||
|
{{ statsGlobales.totalParcelles | number:'1.0-0':'fr' }}</div>
|
||||||
|
<div class="kpi-label">Parcelles Concernées</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-blue" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="file-text" nzTheme="outline"></span>
|
||||||
|
</div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">
|
||||||
|
{{ statsGlobales.totalLiquidations | number:'1.0-0':'fr' }}</div>
|
||||||
|
<div class="kpi-label">Total Liquidations</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-green" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="dollar-circle"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value kpi-value-small">{{ getMontantTotalFormatted() }}
|
||||||
|
</div>
|
||||||
|
<div class="kpi-label">Montant Total Généré</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<nz-card class="kpi-card kpi-card-purple" [nzLoading]="loading">
|
||||||
|
<div class="kpi-content">
|
||||||
|
<div class="kpi-icon"><span nz-icon nzType="check-circle"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="kpi-details">
|
||||||
|
<div class="kpi-value">{{ getTauxCloture() }}<span class="kpi-unit">%</span>
|
||||||
|
</div>
|
||||||
|
<div class="kpi-label">Taux de Clôture</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Bandeaux secondaires ── -->
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stat-banner-card" [nzLoading]="loading">
|
||||||
|
<div class="stat-banner-container">
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-blue">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="fund"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getMontantTFUFormatted() }}</div>
|
||||||
|
<div class="stat-banner-label">Montant TFU</div>
|
||||||
|
<div class="stat-banner-bar">
|
||||||
|
<div class="stat-banner-bar-fill blue"
|
||||||
|
[style.width]="(statsParNature.length > 0 ? statsParNature[0].pourcentage : 0) + '%'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-banner-percent">{{ (statsParNature.length > 0 ? statsParNature[0].pourcentage : 0) }}%
|
||||||
|
du total —
|
||||||
|
{{ (statsParNature.length > 0 ? statsParNature[0].nombreAssujettis : 0) | number:'1.0-0':'fr' }}
|
||||||
|
assujettis</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-green">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="bank"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">{{ getMontantIRFFormatted() }}</div>
|
||||||
|
<div class="stat-banner-label">Montant IRF</div>
|
||||||
|
<div class="stat-banner-bar">
|
||||||
|
<div class="stat-banner-bar-fill green"
|
||||||
|
[style.width]="(statsParNature.length > 1 ? statsParNature[1].pourcentage : 0) + '%'"></div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-banner-percent">{{ (statsParNature.length > 1 ? statsParNature[1].pourcentage : 0) }}%
|
||||||
|
du total —
|
||||||
|
{{ (statsParNature.length > 1 ? statsParNature[1].nombreAssujettis : 0) | number:'1.0-0':'fr' }}
|
||||||
|
assujettis</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-orange">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="apartment"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">
|
||||||
|
{{ statsGlobales.totalBatiments | number:'1.0-0':'fr' }}</div>
|
||||||
|
<div class="stat-banner-label">Bâtiments / Unités logement</div>
|
||||||
|
<div class="stat-banner-bar">
|
||||||
|
<div class="stat-banner-bar-fill orange" [style.width]="'100%'">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-banner-percent">
|
||||||
|
{{ statsGlobales.totalUnitesLogement | number:'1.0-0':'fr' }} unités
|
||||||
|
— {{ statsGlobales.totalPiscines }} piscines</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-banner-divider"></div>
|
||||||
|
|
||||||
|
<div class="stat-banner-item stat-banner-purple">
|
||||||
|
<div class="stat-banner-icon"><span nz-icon nzType="close-circle"
|
||||||
|
nzTheme="outline"></span></div>
|
||||||
|
<div class="stat-banner-body">
|
||||||
|
<div class="stat-banner-value">
|
||||||
|
{{ statsGlobales.totalParcellesExhonerees | number:'1.0-0':'fr' }}
|
||||||
|
</div>
|
||||||
|
<div class="stat-banner-label">Parcelles Exhonérées</div>
|
||||||
|
<div class="stat-banner-bar">
|
||||||
|
<div class="stat-banner-bar-fill purple"
|
||||||
|
[style.width]="(statsGlobales.totalParcellesExhonerees / statsGlobales.totalParcelles * 100) + '%'">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-banner-percent">
|
||||||
|
{{ (statsGlobales.totalParcellesExhonerees / statsGlobales.totalParcelles * 100).toFixed(1) }}%
|
||||||
|
des parcelles</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Statuts sous forme de badges ── -->
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="statuts-pipeline">
|
||||||
|
<div class="pipeline-item pipeline-warning">
|
||||||
|
<div class="pipeline-count">{{ statsGlobales.enCours }}</div>
|
||||||
|
<div class="pipeline-label">En cours</div>
|
||||||
|
<div class="pipeline-bar">
|
||||||
|
<div class="pipeline-bar-fill"
|
||||||
|
[style.width]="(statsGlobales.enCours / statsGlobales.totalLiquidations * 100) + '%'"
|
||||||
|
style="background:#f59e0b"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pipeline-arrow">→</div>
|
||||||
|
<div class="pipeline-item pipeline-cyan ">
|
||||||
|
<div class="pipeline-count">{{ statsGlobales.cloture }}</div>
|
||||||
|
<div class="pipeline-label">Clôturé</div>
|
||||||
|
<div class="pipeline-bar">
|
||||||
|
<div class="pipeline-bar-fill"
|
||||||
|
[style.width]="(statsGlobales.cloture / statsGlobales.totalLiquidations * 100) + '%'"
|
||||||
|
style="background:#06b6d4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pipeline-arrow">→</div>
|
||||||
|
<div class="pipeline-item pipeline-blue">
|
||||||
|
<div class="pipeline-count">{{ statsGlobales.generationAutorisee }}</div>
|
||||||
|
<div class="pipeline-label">Génération autorisée</div>
|
||||||
|
<div class="pipeline-bar">
|
||||||
|
<div class="pipeline-bar-fill"
|
||||||
|
[style.width]="(statsGlobales.generationAutorisee / statsGlobales.totalLiquidations * 100) + '%'"
|
||||||
|
style="background:#1a5890"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pipeline-arrow">→</div>
|
||||||
|
<div class="pipeline-item pipeline-green">
|
||||||
|
<div class="pipeline-count">{{ statsGlobales.genere }}</div>
|
||||||
|
<div class="pipeline-label">Généré</div>
|
||||||
|
<div class="pipeline-bar">
|
||||||
|
<div class="pipeline-bar-fill"
|
||||||
|
[style.width]="(statsGlobales.genere / statsGlobales.totalLiquidations * 100) + '%'"
|
||||||
|
style="background:#10b981"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pipeline-separator"></div>
|
||||||
|
<div class="pipeline-item pipeline-red">
|
||||||
|
<div class="pipeline-count">{{ statsGlobales.rejete }}</div>
|
||||||
|
<div class="pipeline-label">Rejeté</div>
|
||||||
|
<div class="pipeline-bar">
|
||||||
|
<div class="pipeline-bar-fill"
|
||||||
|
[style.width]="(statsGlobales.rejete / statsGlobales.totalLiquidations * 100) + '%'"
|
||||||
|
style="background:#ef4444"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body card-body-review">
|
||||||
|
<div>
|
||||||
|
<!-- ── Onglets thématiques ── -->
|
||||||
|
<div class="thematique-tabs-section">
|
||||||
|
<div class="thematique-tabs">
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'vue-globale'"
|
||||||
|
(click)="activeThematique = 'vue-globale'">
|
||||||
|
<span nz-icon nzType="dashboard" nzTheme="outline"></span> Vue globale
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'evolution'"
|
||||||
|
(click)="activeThematique = 'evolution'">
|
||||||
|
<span nz-icon nzType="line-chart" nzTheme="outline"></span> Évolution annuelle
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'territoire'"
|
||||||
|
(click)="activeThematique = 'territoire'">
|
||||||
|
<span nz-icon nzType="environment" nzTheme="outline"></span> Par territoire
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'dette'"
|
||||||
|
(click)="activeThematique = 'dette'">
|
||||||
|
<span nz-icon nzType="alert" nzTheme="outline"></span> Dette fiscale
|
||||||
|
</button>
|
||||||
|
<button class="ttab" [class.ttab-active]="activeThematique === 'patrimoine'"
|
||||||
|
(click)="activeThematique = 'patrimoine'">
|
||||||
|
<span nz-icon nzType="home" nzTheme="outline"></span> Patrimoine
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── VUE GLOBALE ── -->
|
||||||
|
<div *ngIf="activeThematique === 'vue-globale'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="pie-chart"></span> Statuts des
|
||||||
|
liquidations</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="pieStatutsOptions.series"
|
||||||
|
[chart]="pieStatutsOptions.chart" [labels]="pieStatutsOptions.labels"
|
||||||
|
[colors]="pieStatutsOptions.colors" [legend]="pieStatutsOptions.legend"
|
||||||
|
[plotOptions]="pieStatutsOptions.plotOptions"
|
||||||
|
[dataLabels]="pieStatutsOptions.dataLabels"
|
||||||
|
[responsive]="pieStatutsOptions.responsive">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="pie-chart"></span> Répartition
|
||||||
|
TFU / IRF</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="pieNatureOptions.series"
|
||||||
|
[chart]="pieNatureOptions.chart" [labels]="pieNatureOptions.labels"
|
||||||
|
[colors]="pieNatureOptions.colors" [legend]="pieNatureOptions.legend"
|
||||||
|
[plotOptions]="pieNatureOptions.plotOptions"
|
||||||
|
[dataLabels]="pieNatureOptions.dataLabels"
|
||||||
|
[responsive]="pieNatureOptions.responsive">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
<div class="chart-footer">
|
||||||
|
<div class="chart-summary">
|
||||||
|
<div class="summary-item" *ngFor="let n of statsParNature">
|
||||||
|
<span class="summary-label">{{ n.code }} :</span>
|
||||||
|
<span class="summary-value"
|
||||||
|
[style.color]="n.couleur">{{ formatMontantCourt(n.montant) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="table"></span> Synthèse
|
||||||
|
patrimoine</h3>
|
||||||
|
</div>
|
||||||
|
<div class="synthese-list">
|
||||||
|
<div class="synthese-item">
|
||||||
|
<span class="synthese-icon blue"><span nz-icon
|
||||||
|
nzType="global"></span></span>
|
||||||
|
<div class="synthese-body">
|
||||||
|
<span class="synthese-label">Superficie totale</span>
|
||||||
|
<span
|
||||||
|
class="synthese-val">{{ statsGlobales.superficieTotale | number:'1.0-0':'fr' }}
|
||||||
|
m²</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="synthese-item">
|
||||||
|
<span class="synthese-icon green"><span nz-icon
|
||||||
|
nzType="home"></span></span>
|
||||||
|
<div class="synthese-body">
|
||||||
|
<span class="synthese-label">Parcelles bâties</span>
|
||||||
|
<span
|
||||||
|
class="synthese-val">{{ statsGlobales.totalParcellesBaties | number:'1.0-0':'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="synthese-item">
|
||||||
|
<span class="synthese-icon blue"><span nz-icon
|
||||||
|
nzType="apartment"></span></span>
|
||||||
|
<div class="synthese-body">
|
||||||
|
<span class="synthese-label">Unités de logement</span>
|
||||||
|
<span
|
||||||
|
class="synthese-val">{{ statsGlobales.totalUnitesLogement | number:'1.0-0':'fr' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="synthese-item">
|
||||||
|
<span class="synthese-icon orange"><span nz-icon
|
||||||
|
nzType="control"></span></span>
|
||||||
|
<div class="synthese-body">
|
||||||
|
<span class="synthese-label">Piscines recensées</span>
|
||||||
|
<span class="synthese-val">{{ statsGlobales.totalPiscines }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="synthese-item">
|
||||||
|
<span class="synthese-icon red"><span nz-icon
|
||||||
|
nzType="stop"></span></span>
|
||||||
|
<div class="synthese-body">
|
||||||
|
<span class="synthese-label">Exhonérations</span>
|
||||||
|
<span
|
||||||
|
class="synthese-val">{{ statsGlobales.totalParcellesExhonerees | number:'1.0-0':'fr' }}
|
||||||
|
parc.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── ÉVOLUTION ANNUELLE ── -->
|
||||||
|
<div *ngIf="activeThematique === 'evolution'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="line-chart"></span> Évolution
|
||||||
|
des montants par année</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="lineEvolutionOptions.series"
|
||||||
|
[chart]="lineEvolutionOptions.chart"
|
||||||
|
[xaxis]="lineEvolutionOptions.xaxis"
|
||||||
|
[yaxis]="lineEvolutionOptions.yaxis"
|
||||||
|
[colors]="lineEvolutionOptions.colors"
|
||||||
|
[legend]="lineEvolutionOptions.legend"
|
||||||
|
[stroke]="lineEvolutionOptions.stroke"
|
||||||
|
[markers]="lineEvolutionOptions.markers"
|
||||||
|
[grid]="lineEvolutionOptions.grid"
|
||||||
|
[dataLabels]="lineEvolutionOptions.dataLabels"
|
||||||
|
[tooltip]="lineEvolutionOptions.tooltip"
|
||||||
|
[fill]="lineEvolutionOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="stats-table-card" [nzLoading]="loading">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail par
|
||||||
|
année</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #anneeTable [nzData]="statsParAnnee" [nzShowPagination]="false"
|
||||||
|
nzSize="small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Année</th>
|
||||||
|
<th nzAlign="right">Total</th>
|
||||||
|
<th nzAlign="center">Évol.</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of anneeTable.data">
|
||||||
|
<td><strong>{{ item.annee }}</strong></td>
|
||||||
|
<td nzAlign="right" class="montant-cell">
|
||||||
|
{{ formatMontantCourt(item.montantTotal) }}</td>
|
||||||
|
<td nzAlign="center">
|
||||||
|
<span class="evol-badge" [class.evol-up]="item.evolution > 0"
|
||||||
|
[class.evol-zero]="item.evolution === 0">
|
||||||
|
<span nz-icon
|
||||||
|
[nzType]="item.evolution > 0 ? 'rise' : 'minus'"></span>
|
||||||
|
{{ item.evolution > 0 ? '+' : '' }}{{ item.evolution }}%
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── PAR TERRITOIRE ── -->
|
||||||
|
<div *ngIf="activeThematique === 'territoire'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bar-chart"></span> Montants
|
||||||
|
par commune</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="barCommuneOptions.series"
|
||||||
|
[chart]="barCommuneOptions.chart" [xaxis]="barCommuneOptions.xaxis"
|
||||||
|
[yaxis]="barCommuneOptions.yaxis" [colors]="barCommuneOptions.colors"
|
||||||
|
[legend]="barCommuneOptions.legend" [stroke]="barCommuneOptions.stroke"
|
||||||
|
[markers]="barCommuneOptions.markers" [grid]="barCommuneOptions.grid"
|
||||||
|
[dataLabels]="barCommuneOptions.dataLabels"
|
||||||
|
[plotOptions]="barCommuneOptions.plotOptions"
|
||||||
|
[tooltip]="barCommuneOptions.tooltip" [fill]="barCommuneOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bank"></span> Montants par
|
||||||
|
structure</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="barStructureOptions.series"
|
||||||
|
[chart]="barStructureOptions.chart" [xaxis]="barStructureOptions.xaxis"
|
||||||
|
[yaxis]="barStructureOptions.yaxis"
|
||||||
|
[colors]="barStructureOptions.colors"
|
||||||
|
[legend]="barStructureOptions.legend"
|
||||||
|
[stroke]="barStructureOptions.stroke"
|
||||||
|
[markers]="barStructureOptions.markers"
|
||||||
|
[grid]="barStructureOptions.grid"
|
||||||
|
[dataLabels]="barStructureOptions.dataLabels"
|
||||||
|
[plotOptions]="barStructureOptions.plotOptions"
|
||||||
|
[tooltip]="barStructureOptions.tooltip"
|
||||||
|
[fill]="barStructureOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stats-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail par
|
||||||
|
commune</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #communeTable [nzData]="statsParCommune" [nzPageSize]="5">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Commune</th>
|
||||||
|
<th nzAlign="right">Montant TFU</th>
|
||||||
|
<th nzAlign="right">Montant IRF</th>
|
||||||
|
<th nzAlign="right">Total</th>
|
||||||
|
<th nzAlign="center">Taux recouvrement</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of communeTable.data">
|
||||||
|
<td><span class="fonction-name">{{ item.commune }}</span></td>
|
||||||
|
<td nzAlign="right" class="montant-cell">
|
||||||
|
{{ formatMontantCourt(item.montantTFU) }}</td>
|
||||||
|
<td nzAlign="right" class="montant-cell-green">
|
||||||
|
{{ formatMontantCourt(item.montantIRF) }}</td>
|
||||||
|
<td nzAlign="right"><strong
|
||||||
|
class="montant-total">{{ formatMontantCourt(item.montantTotal) }}</strong>
|
||||||
|
</td>
|
||||||
|
<td nzAlign="center">
|
||||||
|
<nz-progress [nzPercent]="item.tauxRecouvrement"
|
||||||
|
[nzStrokeColor]="getProgressColor(item.tauxRecouvrement)"
|
||||||
|
[nzShowInfo]="true" nzSize="small">
|
||||||
|
</nz-progress>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── DETTE FISCALE ── -->
|
||||||
|
<div *ngIf="activeThematique === 'dette'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-danger">
|
||||||
|
<span nz-icon nzType="warning" nzTheme="fill" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Solde restant total</div>
|
||||||
|
<div class="alert-value">{{ formatMontantCourt(totalSoldeRestant) }}
|
||||||
|
</div>
|
||||||
|
<div class="alert-desc">Montant non encore recouvré sur l'ensemble des
|
||||||
|
périodes.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-success">
|
||||||
|
<span nz-icon nzType="check-circle" nzTheme="fill"
|
||||||
|
class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Montant recouvré total</div>
|
||||||
|
<div class="alert-value">{{ formatMontantCourt(totalDetteRecouvree) }}
|
||||||
|
</div>
|
||||||
|
<div class="alert-desc">Somme des montants effectivement recouvrés.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="alert-card">
|
||||||
|
<div class="alert-content alert-info">
|
||||||
|
<span nz-icon nzType="rise" nzTheme="outline" class="alert-icon"></span>
|
||||||
|
<div class="alert-body">
|
||||||
|
<div class="alert-title">Taux moyen de recouvrement</div>
|
||||||
|
<div class="alert-value">{{ tauxMoyenRecouvrement.toFixed(1) }}%</div>
|
||||||
|
<div class="alert-desc">Moyenne sur toutes les communes et périodes.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bar-chart"></span> Dette
|
||||||
|
fiscale par commune et par année</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content">
|
||||||
|
<apx-chart [series]="barDetteOptions.series" [chart]="barDetteOptions.chart"
|
||||||
|
[xaxis]="barDetteOptions.xaxis" [yaxis]="barDetteOptions.yaxis"
|
||||||
|
[colors]="barDetteOptions.colors" [legend]="barDetteOptions.legend"
|
||||||
|
[stroke]="barDetteOptions.stroke" [markers]="barDetteOptions.markers"
|
||||||
|
[grid]="barDetteOptions.grid" [dataLabels]="barDetteOptions.dataLabels"
|
||||||
|
[plotOptions]="barDetteOptions.plotOptions"
|
||||||
|
[tooltip]="barDetteOptions.tooltip" [fill]="barDetteOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<nz-card class="stats-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail dette
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #detteTable [nzData]="statsDette" [nzPageSize]="6" nzSize="small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Année</th>
|
||||||
|
<th>Commune</th>
|
||||||
|
<th nzAlign="center">Taux</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of detteTable.data">
|
||||||
|
<td><nz-tag [nzColor]="'blue'">{{ item.annee }}</nz-tag></td>
|
||||||
|
<td class="fonction-name">{{ item.commune }}</td>
|
||||||
|
<td nzAlign="center">
|
||||||
|
<nz-progress [nzPercent]="item.tauxRecouvrement"
|
||||||
|
[nzStrokeColor]="getProgressColor(item.tauxRecouvrement)"
|
||||||
|
[nzShowInfo]="true" nzSize="small">
|
||||||
|
</nz-progress>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── PATRIMOINE ── -->
|
||||||
|
<div *ngIf="activeThematique === 'patrimoine'" class="thematique-content">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="bar-chart"></span> TFU par
|
||||||
|
standing de bâtiment</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content" style="min-height: 280px;">
|
||||||
|
<apx-chart [series]="barStandingOptions.series"
|
||||||
|
[chart]="barStandingOptions.chart" [xaxis]="barStandingOptions.xaxis"
|
||||||
|
[yaxis]="barStandingOptions.yaxis" [colors]="barStandingOptions.colors"
|
||||||
|
[legend]="barStandingOptions.legend"
|
||||||
|
[stroke]="barStandingOptions.stroke"
|
||||||
|
[markers]="barStandingOptions.markers" [grid]="barStandingOptions.grid"
|
||||||
|
[dataLabels]="barStandingOptions.dataLabels"
|
||||||
|
[plotOptions]="barStandingOptions.plotOptions"
|
||||||
|
[tooltip]="barStandingOptions.tooltip" [fill]="barStandingOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<nz-card class="chart-card" [nzLoading]="loading">
|
||||||
|
<div class="chart-header">
|
||||||
|
<h3 class="chart-title"><span nz-icon nzType="stop"></span> Exhonérations
|
||||||
|
fiscales</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-content" style="min-height: 280px;">
|
||||||
|
<apx-chart [series]="barExhonerationOptions.series"
|
||||||
|
[chart]="barExhonerationOptions.chart"
|
||||||
|
[xaxis]="barExhonerationOptions.xaxis"
|
||||||
|
[yaxis]="barExhonerationOptions.yaxis"
|
||||||
|
[colors]="barExhonerationOptions.colors"
|
||||||
|
[legend]="barExhonerationOptions.legend"
|
||||||
|
[stroke]="barExhonerationOptions.stroke"
|
||||||
|
[markers]="barExhonerationOptions.markers"
|
||||||
|
[grid]="barExhonerationOptions.grid"
|
||||||
|
[dataLabels]="barExhonerationOptions.dataLabels"
|
||||||
|
[plotOptions]="barExhonerationOptions.plotOptions"
|
||||||
|
[tooltip]="barExhonerationOptions.tooltip"
|
||||||
|
[fill]="barExhonerationOptions.fill">
|
||||||
|
</apx-chart>
|
||||||
|
</div>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<nz-card class="stats-table-card">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3 class="table-title"><span nz-icon nzType="table"></span> Détail par
|
||||||
|
standing</h3>
|
||||||
|
</div>
|
||||||
|
<nz-table #standingTable [nzData]="statsStanding" [nzShowPagination]="false"
|
||||||
|
nzSize="small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Standing</th>
|
||||||
|
<th nzAlign="center">Nb. bâtiments</th>
|
||||||
|
<th nzAlign="right">Montant TFU</th>
|
||||||
|
<th nzAlign="right">Superficie</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let item of standingTable.data">
|
||||||
|
<td><span class="fonction-name">{{ item.standing }}</span></td>
|
||||||
|
<td nzAlign="center"><nz-tag
|
||||||
|
[nzColor]="'blue'">{{ item.nombreBatiments | number:'1.0-0':'fr' }}</nz-tag>
|
||||||
|
</td>
|
||||||
|
<td nzAlign="right" class="montant-cell">
|
||||||
|
{{ formatMontantCourt(item.montantTFU) }}</td>
|
||||||
|
<td nzAlign="right">{{ item.superficie | number:'1.0-0':'fr' }} m²
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</nz-table>
|
||||||
|
</nz-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import {
|
||||||
|
ChartComponent,
|
||||||
|
ApexChart, ApexAxisChartSeries, ApexXAxis, ApexYAxis,
|
||||||
|
ApexLegend, ApexStroke, ApexMarkers, ApexGrid,
|
||||||
|
ApexDataLabels, ApexTooltip, ApexPlotOptions,
|
||||||
|
ApexNonAxisChartSeries, ApexResponsive, ApexFill
|
||||||
|
} from 'ng-apexcharts';
|
||||||
|
|
||||||
|
// ── Interfaces ────────────────────────────────────────────────
|
||||||
|
export interface StatLiquidationGlobale {
|
||||||
|
totalLiquidations: number;
|
||||||
|
enCours: number;
|
||||||
|
generationAutorisee: number;
|
||||||
|
rejete: number;
|
||||||
|
genere: number;
|
||||||
|
cloture: number;
|
||||||
|
totalMontantTFU: number;
|
||||||
|
totalMontantIRF: number;
|
||||||
|
totalMontantGeneral: number;
|
||||||
|
totalParcelles: number;
|
||||||
|
totalParcellesBaties: number;
|
||||||
|
totalParcellesExhonerees: number;
|
||||||
|
totalBatiments: number;
|
||||||
|
totalUnitesLogement: number;
|
||||||
|
totalPiscines: number;
|
||||||
|
superficieTotale: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParAnnee {
|
||||||
|
annee: number;
|
||||||
|
montantTFU: number;
|
||||||
|
montantIRF: number;
|
||||||
|
montantTotal: number;
|
||||||
|
nombreDossiers: number;
|
||||||
|
evolution: number; // % vs année précédente
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParCommune {
|
||||||
|
commune: string;
|
||||||
|
montantTFU: number;
|
||||||
|
montantIRF: number;
|
||||||
|
montantTotal: number;
|
||||||
|
nombreParcelles: number;
|
||||||
|
tauxRecouvrement: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParStructure {
|
||||||
|
structure: string;
|
||||||
|
montantTFU: number;
|
||||||
|
montantIRF: number;
|
||||||
|
montantTotal: number;
|
||||||
|
nombreDossiers: number;
|
||||||
|
tauxRecouvrement: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParNatureImpot {
|
||||||
|
nature: string;
|
||||||
|
code: 'TFU' | 'IRF';
|
||||||
|
couleur: string;
|
||||||
|
montant: number;
|
||||||
|
pourcentage: number;
|
||||||
|
nombreAssujettis: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatDetteFiscale {
|
||||||
|
annee: number;
|
||||||
|
commune: string;
|
||||||
|
detteInitiale: number;
|
||||||
|
detteRecouvree: number;
|
||||||
|
soldeRestant: number;
|
||||||
|
tauxRecouvrement: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatParStanding {
|
||||||
|
standing: string;
|
||||||
|
nombreBatiments: number;
|
||||||
|
montantTFU: number;
|
||||||
|
superficie: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatExhoneration {
|
||||||
|
categorie: string;
|
||||||
|
nombreExhoneres: number;
|
||||||
|
montantExhonere: number;
|
||||||
|
pourcentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PieChartOptions = {
|
||||||
|
series: ApexNonAxisChartSeries; chart: ApexChart;
|
||||||
|
labels: string[]; colors: string[]; legend: ApexLegend;
|
||||||
|
plotOptions: ApexPlotOptions; dataLabels: ApexDataLabels;
|
||||||
|
responsive: ApexResponsive[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BarChartOptions = {
|
||||||
|
series: ApexAxisChartSeries; chart: ApexChart;
|
||||||
|
xaxis: ApexXAxis; yaxis: ApexYAxis | ApexYAxis[];
|
||||||
|
colors: string[]; legend: ApexLegend; stroke: ApexStroke;
|
||||||
|
markers: ApexMarkers; grid: ApexGrid; dataLabels: ApexDataLabels;
|
||||||
|
tooltip: ApexTooltip; plotOptions: ApexPlotOptions; fill: ApexFill;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LineChartOptions = {
|
||||||
|
series: ApexAxisChartSeries; chart: ApexChart;
|
||||||
|
xaxis: ApexXAxis; yaxis: ApexYAxis; colors: string[];
|
||||||
|
legend: ApexLegend; stroke: ApexStroke; markers: ApexMarkers;
|
||||||
|
grid: ApexGrid; dataLabels: ApexDataLabels; tooltip: ApexTooltip;
|
||||||
|
fill: ApexFill;
|
||||||
|
};
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sommaire-consultation',
|
||||||
|
templateUrl: './sommaire-consultation.component.html',
|
||||||
|
styleUrls: ['./sommaire-consultation.component.css'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class SommaireConsultationComponent implements OnInit {
|
||||||
|
|
||||||
|
@ViewChild('chart') chart!: ChartComponent;
|
||||||
|
|
||||||
|
loading = false;
|
||||||
|
activeThematique = 'vue-globale';
|
||||||
|
|
||||||
|
readonly statusLabels: { [key: string]: string } = {
|
||||||
|
'EN_COURS': 'EN COURS',
|
||||||
|
'GENERATION_AUTORISE': 'GÉNÉRATION AUTORISÉE',
|
||||||
|
'REJETE': 'REJETÉ',
|
||||||
|
'GENERE': 'GÉNÉRÉ',
|
||||||
|
'CLOTURE': 'CLÔTURÉ'
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly statusColors: { [key: string]: string } = {
|
||||||
|
'EN_COURS': '#f59e0b',
|
||||||
|
'GENERATION_AUTORISE': '#1a5890',
|
||||||
|
'REJETE': '#ef4444',
|
||||||
|
'GENERE': '#06b6d4',
|
||||||
|
'CLOTURE': '#10b981'
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Data ──────────────────────────────────────────────────────────────
|
||||||
|
statsGlobales: StatLiquidationGlobale = this.initStatsGlobales();
|
||||||
|
statsParAnnee: StatParAnnee[] = [];
|
||||||
|
statsParCommune: StatParCommune[] = [];
|
||||||
|
statsParStructure: StatParStructure[] = [];
|
||||||
|
statsParNature: StatParNatureImpot[] = [];
|
||||||
|
statsDette: StatDetteFiscale[] = [];
|
||||||
|
statsStanding: StatParStanding[] = [];
|
||||||
|
statsExhoneration: StatExhoneration[] = [];
|
||||||
|
|
||||||
|
// ── Charts ────────────────────────────────────────────────────────────
|
||||||
|
pieStatutsOptions: Partial<PieChartOptions> = {};
|
||||||
|
pieNatureOptions: Partial<PieChartOptions> = {};
|
||||||
|
lineEvolutionOptions: Partial<LineChartOptions> = {};
|
||||||
|
barCommuneOptions: Partial<BarChartOptions> = {};
|
||||||
|
barStructureOptions: Partial<BarChartOptions> = {};
|
||||||
|
barStandingOptions: Partial<BarChartOptions> = {};
|
||||||
|
barDetteOptions: Partial<BarChartOptions> = {};
|
||||||
|
barExhonerationOptions: Partial<BarChartOptions> = {};
|
||||||
|
|
||||||
|
constructor(private message: NzMessageService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void { this.loadData(); }
|
||||||
|
|
||||||
|
initStatsGlobales(): StatLiquidationGlobale {
|
||||||
|
return {
|
||||||
|
totalLiquidations: 0, enCours: 0, generationAutorisee: 0,
|
||||||
|
rejete: 0, genere: 0, cloture: 0,
|
||||||
|
totalMontantTFU: 0, totalMontantIRF: 0, totalMontantGeneral: 0,
|
||||||
|
totalParcelles: 0, totalParcellesBaties: 0, totalParcellesExhonerees: 0,
|
||||||
|
totalBatiments: 0, totalUnitesLogement: 0, totalPiscines: 0,
|
||||||
|
superficieTotale: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData(): void {
|
||||||
|
this.loading = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
this.statsGlobales = {
|
||||||
|
totalLiquidations: 142, enCours: 28, generationAutorisee: 15,
|
||||||
|
rejete: 8, genere: 61, cloture: 30,
|
||||||
|
totalMontantTFU: 487_650_000, totalMontantIRF: 213_440_000,
|
||||||
|
totalMontantGeneral: 701_090_000,
|
||||||
|
totalParcelles: 4820, totalParcellesBaties: 2974,
|
||||||
|
totalParcellesExhonerees: 312, totalBatiments: 3210,
|
||||||
|
totalUnitesLogement: 8640, totalPiscines: 47,
|
||||||
|
superficieTotale: 1_248_600
|
||||||
|
};
|
||||||
|
|
||||||
|
this.statsParAnnee = [
|
||||||
|
{ annee: 2019, montantTFU: 310_000_000, montantIRF: 120_000_000, montantTotal: 430_000_000, nombreDossiers: 18, evolution: 0 },
|
||||||
|
{ annee: 2020, montantTFU: 345_000_000, montantIRF: 138_000_000, montantTotal: 483_000_000, nombreDossiers: 24, evolution: 12.3 },
|
||||||
|
{ annee: 2021, montantTFU: 389_000_000, montantIRF: 155_000_000, montantTotal: 544_000_000, nombreDossiers: 31, evolution: 12.6 },
|
||||||
|
{ annee: 2022, montantTFU: 421_000_000, montantIRF: 178_000_000, montantTotal: 599_000_000, nombreDossiers: 38, evolution: 10.1 },
|
||||||
|
{ annee: 2023, montantTFU: 456_000_000, montantIRF: 196_000_000, montantTotal: 652_000_000, nombreDossiers: 45, evolution: 8.8 },
|
||||||
|
{ annee: 2024, montantTFU: 487_650_000, montantIRF: 213_440_000, montantTotal: 701_090_000, nombreDossiers: 54, evolution: 7.5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsParCommune = [
|
||||||
|
{ commune: 'Cotonou', montantTFU: 198_000_000, montantIRF: 87_000_000, montantTotal: 285_000_000, nombreParcelles: 1550, tauxRecouvrement: 82 },
|
||||||
|
{ commune: 'Porto-Novo', montantTFU: 112_000_000, montantIRF: 48_000_000, montantTotal: 160_000_000, nombreParcelles: 1150, tauxRecouvrement: 75 },
|
||||||
|
{ commune: 'Parakou', montantTFU: 87_000_000, montantIRF: 38_000_000, montantTotal: 125_000_000, nombreParcelles: 900, tauxRecouvrement: 68 },
|
||||||
|
{ commune: 'Abomey-Calavi', montantTFU: 68_000_000, montantIRF: 28_000_000, montantTotal: 96_000_000, nombreParcelles: 700, tauxRecouvrement: 71 },
|
||||||
|
{ commune: 'Natitingou', montantTFU: 22_650_000, montantIRF: 12_440_000, montantTotal: 35_090_000, nombreParcelles: 520, tauxRecouvrement: 79 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsParStructure = [
|
||||||
|
{ structure: 'DGI Cotonou', montantTFU: 198_000_000, montantIRF: 87_000_000, montantTotal: 285_000_000, nombreDossiers: 42, tauxRecouvrement: 82 },
|
||||||
|
{ structure: 'DGI Porto-Novo', montantTFU: 112_000_000, montantIRF: 48_000_000, montantTotal: 160_000_000, nombreDossiers: 35, tauxRecouvrement: 75 },
|
||||||
|
{ structure: 'Centre Impôts Sud', montantTFU: 87_000_000, montantIRF: 38_000_000, montantTotal: 125_000_000, nombreDossiers: 28, tauxRecouvrement: 68 },
|
||||||
|
{ structure: 'Centre Impôts Nord', montantTFU: 68_000_000, montantIRF: 28_000_000, montantTotal: 96_000_000, nombreDossiers: 22, tauxRecouvrement: 71 },
|
||||||
|
{ structure: 'Service Calavi', montantTFU: 22_650_000, montantIRF: 12_440_000, montantTotal: 35_090_000, nombreDossiers: 15, tauxRecouvrement: 79 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsParNature = [
|
||||||
|
{ nature: 'TFU — Taxe Foncière Unique', code: 'TFU', couleur: '#1a5890', montant: 487_650_000, pourcentage: 69.6, nombreAssujettis: 3210 },
|
||||||
|
{ nature: 'IRF — Impôt sur Revenu Foncier', code: 'IRF', couleur: '#10b981', montant: 213_440_000, pourcentage: 30.4, nombreAssujettis: 1840 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsDette = [
|
||||||
|
{ annee: 2022, commune: 'Cotonou', detteInitiale: 285_000_000, detteRecouvree: 233_700_000, soldeRestant: 51_300_000, tauxRecouvrement: 82 },
|
||||||
|
{ annee: 2022, commune: 'Porto-Novo', detteInitiale: 160_000_000, detteRecouvree: 120_000_000, soldeRestant: 40_000_000, tauxRecouvrement: 75 },
|
||||||
|
{ annee: 2023, commune: 'Cotonou', detteInitiale: 310_000_000, detteRecouvree: 264_550_000, soldeRestant: 45_450_000, tauxRecouvrement: 85 },
|
||||||
|
{ annee: 2023, commune: 'Porto-Novo', detteInitiale: 175_000_000, detteRecouvree: 140_000_000, soldeRestant: 35_000_000, tauxRecouvrement: 80 },
|
||||||
|
{ annee: 2024, commune: 'Cotonou', detteInitiale: 340_000_000, detteRecouvree: 278_000_000, soldeRestant: 62_000_000, tauxRecouvrement: 82 },
|
||||||
|
{ annee: 2024, commune: 'Abomey-Calavi', detteInitiale: 96_000_000, detteRecouvree: 68_160_000, soldeRestant: 27_840_000, tauxRecouvrement: 71 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsStanding = [
|
||||||
|
{ standing: 'Standing A', nombreBatiments: 420, montantTFU: 198_000_000, superficie: 312_000 },
|
||||||
|
{ standing: 'Standing B', nombreBatiments: 860, montantTFU: 156_000_000, superficie: 487_000 },
|
||||||
|
{ standing: 'Standing C', nombreBatiments: 1240, montantTFU: 89_650_000, superficie: 298_000 },
|
||||||
|
{ standing: 'Standing D', nombreBatiments: 690, montantTFU: 44_000_000, superficie: 151_600 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.statsExhoneration = [
|
||||||
|
{ categorie: 'Parcelles exhonérées', nombreExhoneres: 312, montantExhonere: 48_200_000, pourcentage: 6.5 },
|
||||||
|
{ categorie: 'Bâtiments exhonérés', nombreExhoneres: 184, montantExhonere: 31_500_000, pourcentage: 5.7 },
|
||||||
|
{ categorie: 'Unités logement exhonérées', nombreExhoneres: 423, montantExhonere: 22_800_000, pourcentage: 4.9 },
|
||||||
|
];
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
this.buildAllCharts();
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAllCharts(): void {
|
||||||
|
this.buildPieStatuts();
|
||||||
|
this.buildPieNature();
|
||||||
|
this.buildLineEvolution();
|
||||||
|
this.buildBarCommune();
|
||||||
|
this.buildBarStructure();
|
||||||
|
this.buildBarStanding();
|
||||||
|
this.buildBarDette();
|
||||||
|
this.buildBarExhoneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPieStatuts(): void {
|
||||||
|
const statuts = [
|
||||||
|
{ label: 'En cours', val: this.statsGlobales.enCours, col: '#f59e0b' },
|
||||||
|
{ label: 'Génération autorisée', val: this.statsGlobales.generationAutorisee, col: '#1a5890' },
|
||||||
|
{ label: 'Rejeté', val: this.statsGlobales.rejete, col: '#ef4444' },
|
||||||
|
{ label: 'Généré', val: this.statsGlobales.genere, col: '#06b6d4' },
|
||||||
|
{ label: 'Clôturé', val: this.statsGlobales.cloture, col: '#10b981' },
|
||||||
|
];
|
||||||
|
this.pieStatutsOptions = {
|
||||||
|
series: statuts.map(s => s.val),
|
||||||
|
chart: { type: 'donut', height: 320, fontFamily: 'Inter, sans-serif', animations: { enabled: true, speed: 700 } },
|
||||||
|
labels: statuts.map(s => s.label),
|
||||||
|
colors: statuts.map(s => s.col),
|
||||||
|
legend: { position: 'bottom', fontSize: '12px' },
|
||||||
|
plotOptions: {
|
||||||
|
pie: {
|
||||||
|
donut: {
|
||||||
|
size: '65%',
|
||||||
|
labels: {
|
||||||
|
show: true,
|
||||||
|
total: {
|
||||||
|
show: true, label: 'Total', fontSize: '13px',
|
||||||
|
formatter: (w: any) => w.globals.seriesTotals.reduce((a: number, b: number) => a + b, 0) + ' liquid.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
responsive: [{ breakpoint: 480, options: { chart: { height: 260 } } }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPieNature(): void {
|
||||||
|
this.pieNatureOptions = {
|
||||||
|
series: this.statsParNature.map(n => n.montant),
|
||||||
|
chart: { type: 'pie', height: 320, fontFamily: 'Inter, sans-serif', animations: { enabled: true, speed: 700 } },
|
||||||
|
labels: this.statsParNature.map(n => n.nature),
|
||||||
|
colors: this.statsParNature.map(n => n.couleur),
|
||||||
|
legend: { position: 'bottom', fontSize: '12px' },
|
||||||
|
plotOptions: { pie: { expandOnClick: true } },
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true, formatter: (val: number) => val.toFixed(1) + '%',
|
||||||
|
style: { fontSize: '12px', fontWeight: 700, colors: ['#fff'] }
|
||||||
|
},
|
||||||
|
responsive: [{ breakpoint: 480, options: { chart: { height: 260 } } }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildLineEvolution(): void {
|
||||||
|
this.lineEvolutionOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'TFU (FCFA)', data: this.statsParAnnee.map(a => a.montantTFU) },
|
||||||
|
{ name: 'IRF (FCFA)', data: this.statsParAnnee.map(a => a.montantIRF) },
|
||||||
|
{ name: 'Total (FCFA)', data: this.statsParAnnee.map(a => a.montantTotal) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'line', height: 360, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParAnnee.map(a => a.annee.toString()),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: (val: number) => this.formatMontantCourt(val),
|
||||||
|
style: { colors: '#6b7280', fontSize: '11px' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors: ['#1a5890', '#10b981', '#f59e0b'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { curve: 'smooth', width: 3 },
|
||||||
|
markers: { size: 6, strokeWidth: 2, strokeColors: '#fff', hover: { size: 8 } },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: {
|
||||||
|
shared: true, intersect: false,
|
||||||
|
y: { formatter: (val: number) => this.formatMontant(val) }
|
||||||
|
},
|
||||||
|
fill: { type: 'solid', opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBarCommune(): void {
|
||||||
|
this.barCommuneOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'TFU', data: this.statsParCommune.map(c => c.montantTFU) },
|
||||||
|
{ name: 'IRF', data: this.statsParCommune.map(c => c.montantIRF) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'bar', height: 340, stacked: true, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '55%', borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParCommune.map(c => c.commune),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: (val: number) => this.formatMontantCourt(val),
|
||||||
|
style: { colors: '#6b7280' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors: ['#1a5890', '#10b981'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: false, width: 0, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => this.formatMontant(val) } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBarStructure(): void {
|
||||||
|
this.barStructureOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'TFU', data: this.statsParStructure.map(s => s.montantTFU) },
|
||||||
|
{ name: 'IRF', data: this.statsParStructure.map(s => s.montantIRF) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'bar', height: 340, stacked: true, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
plotOptions: { bar: { horizontal: true, borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsParStructure.map(s => s.structure),
|
||||||
|
labels: {
|
||||||
|
formatter: (val: string) => this.formatMontantCourt(Number(val)),
|
||||||
|
style: { colors: '#6b7280', fontSize: '11px' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: { labels: { style: { colors: '#6b7280', fontSize: '12px' } } },
|
||||||
|
colors: ['#1a5890', '#10b981'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: false, width: 0, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: {
|
||||||
|
enabled: true,
|
||||||
|
formatter: (val: number) => val > 10_000_000 ? this.formatMontantCourt(val) : '',
|
||||||
|
style: { fontSize: '10px', fontWeight: 600, colors: ['#fff'] }
|
||||||
|
},
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => this.formatMontant(val) } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBarStanding(): void {
|
||||||
|
this.barStandingOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Montant TFU', data: this.statsStanding.map(s => s.montantTFU) },
|
||||||
|
{ name: 'Nb. bâtiments', data: this.statsStanding.map(s => s.nombreBatiments) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'bar', height: 320, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: false }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '50%', borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsStanding.map(s => s.standing),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '12px' } }
|
||||||
|
},
|
||||||
|
yaxis: [
|
||||||
|
{
|
||||||
|
title: { text: 'Montant (FCFA)', style: { color: '#1a5890' } },
|
||||||
|
labels: { formatter: (val: number) => this.formatMontantCourt(val) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opposite: true, title: { text: 'Nb. bâtiments', style: { color: '#10b981' } },
|
||||||
|
labels: { formatter: (val: number) => val.toFixed(0) }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
colors: ['#1a5890', '#10b981'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: true, width: [0, 2], colors: ['transparent', '#10b981'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBarDette(): void {
|
||||||
|
const annees = [...new Set(this.statsDette.map(d => d.annee.toString()))];
|
||||||
|
const communes = [...new Set(this.statsDette.map(d => d.commune))];
|
||||||
|
|
||||||
|
this.barDetteOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Dette initiale', data: this.statsDette.map(d => d.detteInitiale) },
|
||||||
|
{ name: 'Recouvrée', data: this.statsDette.map(d => d.detteRecouvree) },
|
||||||
|
{ name: 'Solde restant', data: this.statsDette.map(d => d.soldeRestant) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'bar', height: 360, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: true }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '60%', borderRadius: 3 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsDette.map(d => d.annee + '\n' + d.commune),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '10px' } }
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: (val: number) => this.formatMontantCourt(val),
|
||||||
|
style: { colors: '#6b7280' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors: ['#1a5890', '#10b981', '#ef4444'],
|
||||||
|
legend: { position: 'bottom', fontSize: '13px' },
|
||||||
|
stroke: { show: true, width: 2, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false, y: { formatter: (val: number) => this.formatMontant(val) } },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
buildBarExhoneration(): void {
|
||||||
|
this.barExhonerationOptions = {
|
||||||
|
series: [
|
||||||
|
{ name: 'Nombre exhonérés', data: this.statsExhoneration.map(e => e.nombreExhoneres) },
|
||||||
|
{ name: 'Montant (FCFA)', data: this.statsExhoneration.map(e => e.montantExhonere) },
|
||||||
|
],
|
||||||
|
chart: {
|
||||||
|
type: 'bar', height: 280, fontFamily: 'Inter, sans-serif',
|
||||||
|
toolbar: { show: false }, animations: { enabled: true, speed: 700 }
|
||||||
|
},
|
||||||
|
plotOptions: { bar: { horizontal: false, columnWidth: '50%', borderRadius: 4 } },
|
||||||
|
xaxis: {
|
||||||
|
categories: this.statsExhoneration.map(e => e.categorie),
|
||||||
|
labels: { style: { colors: '#6b7280', fontSize: '11px' } }
|
||||||
|
},
|
||||||
|
yaxis: [
|
||||||
|
{ labels: { formatter: (val: number) => val.toFixed(0) } },
|
||||||
|
{ opposite: true, labels: { formatter: (val: number) => this.formatMontantCourt(val) } }
|
||||||
|
],
|
||||||
|
colors: ['#1a5890', '#f59e0b'],
|
||||||
|
legend: { position: 'bottom', fontSize: '12px' },
|
||||||
|
stroke: { show: true, width: 2, colors: ['transparent'] },
|
||||||
|
markers: { size: 0 },
|
||||||
|
grid: { borderColor: '#e0ecf8', strokeDashArray: 4 },
|
||||||
|
dataLabels: { enabled: false },
|
||||||
|
tooltip: { shared: true, intersect: false },
|
||||||
|
fill: { opacity: 1 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilitaires ───────────────────────────────────────────────────────
|
||||||
|
formatMontant(val: number): string {
|
||||||
|
return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'XOF', maximumFractionDigits: 0 }).format(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatMontantCourt(val: number): string {
|
||||||
|
if (val >= 1_000_000_000) return (val / 1_000_000_000).toFixed(1) + ' Mrd';
|
||||||
|
if (val >= 1_000_000) return (val / 1_000_000).toFixed(1) + ' M';
|
||||||
|
if (val >= 1_000) return (val / 1_000).toFixed(0) + ' K';
|
||||||
|
return val.toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTauxCloture(): number {
|
||||||
|
const total = this.statsGlobales.totalLiquidations;
|
||||||
|
return total > 0 ? Math.round((this.statsGlobales.cloture / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTauxRejet(): number {
|
||||||
|
const total = this.statsGlobales.totalLiquidations;
|
||||||
|
return total > 0 ? Math.round((this.statsGlobales.rejete / total) * 100) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProgressColor(percent: number): string {
|
||||||
|
if (percent >= 80) return '#10b981';
|
||||||
|
if (percent >= 65) return '#f59e0b';
|
||||||
|
return '#ef4444';
|
||||||
|
}
|
||||||
|
|
||||||
|
getMontantTotalFormatted(): string { return this.formatMontant(this.statsGlobales.totalMontantGeneral); }
|
||||||
|
getMontantTFUFormatted(): string { return this.formatMontant(this.statsGlobales.totalMontantTFU); }
|
||||||
|
getMontantIRFFormatted(): string { return this.formatMontant(this.statsGlobales.totalMontantIRF); }
|
||||||
|
|
||||||
|
refreshData(): void { this.loadData(); }
|
||||||
|
|
||||||
|
get totalSoldeRestant(): number {
|
||||||
|
return this.statsDette.reduce((a, d) => a + d.soldeRestant, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalDetteRecouvree(): number {
|
||||||
|
return this.statsDette.reduce((a, d) => a + d.detteRecouvree, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get tauxMoyenRecouvrement(): number {
|
||||||
|
if (!this.statsDette.length) return 0;
|
||||||
|
return this.statsDette.reduce((a, d) => a + d.tauxRecouvrement, 0) / this.statsDette.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnneeList(): number[] {
|
||||||
|
const annees = new Set<number>();
|
||||||
|
this.statsParAnnee.forEach(s => annees.add(s.annee));
|
||||||
|
return Array.from(annees).sort((a, b) => b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommuneList(): string[] {
|
||||||
|
const communes = new Set<string>();
|
||||||
|
this.statsParCommune.forEach(s => communes.add(s.commune));
|
||||||
|
return Array.from(communes).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
getStructureList(): string[] {
|
||||||
|
const structures = new Set<string>();
|
||||||
|
this.statsParStructure.forEach(s => structures.add(s.structure));
|
||||||
|
return Array.from(structures).sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/app/office/dashbord/dashbord-routing.module.ts
Normal file
13
src/app/office/dashbord/dashbord-routing.module.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { DashbordComponent } from './dashbord.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: 'dashbord', component: DashbordComponent },
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class DashbordRoutingModule { }
|
||||||
0
src/app/office/dashbord/dashbord.component.css
Normal file
0
src/app/office/dashbord/dashbord.component.css
Normal file
308
src/app/office/dashbord/dashbord.component.html
Normal file
308
src/app/office/dashbord/dashbord.component.html
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
<!--<div class="row" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-6 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">ÉVOLUTION DES ENQUÊTES</h4>
|
||||||
|
<p class="card-description">
|
||||||
|
Statistique de l'évolution des enquêtes par statut
|
||||||
|
</p>
|
||||||
|
<div class="table-responsive text-center" style="overflow-x: unset!important;">
|
||||||
|
|
||||||
|
<div class="text-center" id="chart" *ngIf="chartOptions && chartOptions.series">
|
||||||
|
<apx-chart [series]="chartOptions.series" [chart]="chartOptions.chart" [labels]="chartOptions.labels"
|
||||||
|
[responsive]="chartOptions.responsive" [fill]="fillColorList"></apx-chart>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">ÉVALUATION DES PERSONNES</h4>
|
||||||
|
<p class="card-description">
|
||||||
|
Statistique de l'évaluation du nombre de personne contacté par catégorie.
|
||||||
|
</p>
|
||||||
|
<div class="table-responsive text-center" style="overflow-x: unset!important;">
|
||||||
|
|
||||||
|
<div class="text-center" id="chartPersonne" *ngIf="chartPersonneOptions && chartPersonneOptions.series">
|
||||||
|
<apx-chart [series]="chartPersonneOptions.series" [chart]="chartPersonneOptions.chart" [labels]="chartPersonneOptions.labels"
|
||||||
|
[responsive]="chartPersonneOptions.responsive" [fill]="fillColorList"></apx-chart>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card card-statistics" style="border-bottom: solid 3px #e0bb62;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="clearfix">
|
||||||
|
<div class="float-left">
|
||||||
|
<i class="mdi mdi-download text-warning icon-lg"></i>
|
||||||
|
</div>
|
||||||
|
<div class="float-right">
|
||||||
|
<div class="fluid-container">
|
||||||
|
<h3 class="font-weight-medium text-right mb-0">
|
||||||
|
{{ (nombreSynchronise ? nombreSynchronise.nombre : 0) | number:'':'fr-FR' }} </h3>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0 text-right text-warning" style="font-size: 1.1em;">Finalisées <i
|
||||||
|
class="mdi mdi-arrow-up"></i> </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mt-3 mb-0 text-center">
|
||||||
|
<i class="mdi mdi-bookmark-outline mr-1" aria-hidden="true"></i> Nombre total des enquêtes synchronisées
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card card-statistics" style="border-bottom: solid 3px #00ce68;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="clearfix">
|
||||||
|
<div class="float-left">
|
||||||
|
<i class="mdi mdi-check-circle text-success icon-lg"></i>
|
||||||
|
</div>
|
||||||
|
<div class="float-right">
|
||||||
|
<div class="fluid-container">
|
||||||
|
<h3 class="font-weight-medium text-right mb-0">
|
||||||
|
{{ (nombreValide ? nombreValide.nombre : 0) | number:'':'fr-FR' }} </h3>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0 text-right text-success" style="font-size: 1.1em;">Validées <i class="mdi mdi-arrow-up"></i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mt-3 mb-0 text-center">
|
||||||
|
<i class="mdi mdi-bookmark-outline mr-1" aria-hidden="true"></i> Nombre total des enquêtes validées
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card card-statistics" style="border-bottom: solid 3px #e65251;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="clearfix">
|
||||||
|
<div class="float-left">
|
||||||
|
<i class="mdi mdi-cancel text-danger icon-lg"></i>
|
||||||
|
</div>
|
||||||
|
<div class="float-right">
|
||||||
|
<div class="fluid-container">
|
||||||
|
<h3 class="font-weight-medium text-right mb-0">
|
||||||
|
{{ (nombreRejete ? nombreRejete.nombre : 0) | number:'':'fr-FR' }} </h3>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0 text-right text-danger" style="font-size: 1.1em;">Rejetées <i class="mdi mdi-arrow-down"></i>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mt-3 mb-0 text-center">
|
||||||
|
<i class="mdi mdi-bookmark-outline mr-1" aria-hidden="true"></i> Nombre total des enquêtes rejetées
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row mt-3" [ngClass]="isActionInProgress ? 'hidden-for-loading': 'visible-for-loading'">
|
||||||
|
<div class="col-lg-12 grid-margin stretch-card">
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title" style="font-size: 18px!important;font-weight: 900;">POINT DES ENQUÊTES</h4>
|
||||||
|
<p class="card-description">
|
||||||
|
Point sur l'évolution du nombre des enquêtes par statut
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<nz-tabset *ngIf="isRoles(['ROLE_ADMIN'])">
|
||||||
|
|
||||||
|
<nz-tab nzTitle="Point des enquêtes des centres d'impôts">
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Centre d'impôts
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête total
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête validé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête synchronisé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête rejeté
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of structureEnqueteList; let i=index" class="border-bottom-light">
|
||||||
|
<td>
|
||||||
|
{{ todo.structure }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-dark" style="font-size: 16px;">
|
||||||
|
{{ todo.total | number:'':'fr-FR' }}
|
||||||
|
<i class="mdi mdi-arrow-right"> </i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-success" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.valide | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-info" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.synchronise | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-danger" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-down"> </i> {{ todo.rejet | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</nz-tab>
|
||||||
|
|
||||||
|
<nz-tab nzTitle="Point des enquêtes par arrondissement">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="did-floating-label-content mt-3">
|
||||||
|
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="Selectionner la commune"
|
||||||
|
(ngModelChange)="filterArrondissementByCommune($event)" [(ngModel)]="commune"
|
||||||
|
[compareWith]="compareFn">
|
||||||
|
<nz-option *ngFor="let item of communeList" [nzLabel]="item.nom" [nzValue]="item"></nz-option>
|
||||||
|
</nz-select>
|
||||||
|
<label class="did-floating-label" style="top: -15px;"> Commune <span class="text-danger"> *</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Arrondissement
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête total
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête validé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête synchronisé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête rejeté
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of arrondissementEnqueteList; let i=index" class="border-bottom-light">
|
||||||
|
<td>
|
||||||
|
{{ todo.arrondissement }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-dark" style="font-size: 16px;">
|
||||||
|
{{ todo.total | number:'':'fr-FR' }}
|
||||||
|
<i class="mdi mdi-arrow-right"> </i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-success" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.valide | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-info" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.synchronise | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-danger" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-down"> </i> {{ todo.rejet | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</nz-tab>
|
||||||
|
|
||||||
|
</nz-tabset>
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table table-striped" *ngIf="isRoles(['ROLE_SUPERVISEUR', 'ROLE_DIRECTEUR', 'ROLE_ENQUETEUR'])">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Bloc
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête total
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête validé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête synchronisé
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
Nombre d'enquête rejeté
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let todo of blocEnqueteList; let i=index" class="border-bottom-light">
|
||||||
|
<td>
|
||||||
|
{{ todo.bloc }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-dark" style="font-size: 16px;">
|
||||||
|
{{ (todo.valide + todo.synchronise + todo.rejet) | number:'':'fr-FR' }}
|
||||||
|
<i class="mdi mdi-arrow-right"> </i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-success" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.valide | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-info" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-up"> </i> {{ todo.synchronise | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge badge-danger" style="font-size: 16px;">
|
||||||
|
<i class="mdi mdi-arrow-down"> </i> {{ todo.rejet | number:'':'fr-FR' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
235
src/app/office/dashbord/dashbord.component.ts
Normal file
235
src/app/office/dashbord/dashbord.component.ts
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { JwtHelperService } from '@auth0/angular-jwt';
|
||||||
|
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||||
|
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { CrudService } from 'src/app/crud.service';
|
||||||
|
import { GlobalService } from 'src/app/global.service';
|
||||||
|
import { TokenStorage } from 'src/app/utilitaire/token-storage';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
|
import { ApexFill, ChartComponent } from "ng-apexcharts";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ApexNonAxisChartSeries,
|
||||||
|
ApexResponsive,
|
||||||
|
ApexChart
|
||||||
|
} from "ng-apexcharts";
|
||||||
|
|
||||||
|
export type ChartOptions = {
|
||||||
|
series: ApexNonAxisChartSeries;
|
||||||
|
chart: ApexChart;
|
||||||
|
responsive: ApexResponsive[];
|
||||||
|
labels: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dashbord',
|
||||||
|
templateUrl: './dashbord.component.html',
|
||||||
|
styleUrls: ['./dashbord.component.css']
|
||||||
|
})
|
||||||
|
export class DashbordComponent implements OnInit {
|
||||||
|
|
||||||
|
|
||||||
|
communeList: any[] = [];
|
||||||
|
commune: any = null;
|
||||||
|
user: any = null;
|
||||||
|
|
||||||
|
isActionInProgress = false;
|
||||||
|
|
||||||
|
blocEnqueteList: any[] = [
|
||||||
|
{ libelle: 'C1373717633131', nombre: 120 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 230 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 50 }
|
||||||
|
];
|
||||||
|
|
||||||
|
arrondissementEnqueteList: any[] = [
|
||||||
|
{ libelle: 'C1373717633131', nombre: 120 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 230 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 50 }
|
||||||
|
];
|
||||||
|
|
||||||
|
structureEnqueteList: any[] = [
|
||||||
|
{ libelle: 'C1373717633131', nombre: 120 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 230 },
|
||||||
|
{ libelle: 'C3286242849482', nombre: 50 }
|
||||||
|
];
|
||||||
|
|
||||||
|
blocEnqueteAllStatutList: any[] = [
|
||||||
|
{ libelle: 'C1373717633131', valide: 120, rejete: 12, synchronise: 23 },
|
||||||
|
{ libelle: 'C3286242849482', valide: 120, rejete: 12, synchronise: 23 },
|
||||||
|
{ libelle: 'C3286242849482', valide: 120, rejete: 12, synchronise: 23 }
|
||||||
|
];
|
||||||
|
|
||||||
|
@ViewChild("chart") chart!: ChartComponent;
|
||||||
|
public chartOptions!: Partial<ChartOptions>;
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild("chartPersonne") chartPersonne!: ChartComponent;
|
||||||
|
public chartPersonneOptions!: Partial<ChartOptions>;
|
||||||
|
|
||||||
|
nombreValide: any = null;
|
||||||
|
nombreRejete: any = null;
|
||||||
|
nombreSynchronise: any = null;
|
||||||
|
|
||||||
|
fillColorList: ApexFill = {
|
||||||
|
colors: ["#00ce68", "#e0bb62", "#e65251"]
|
||||||
|
};
|
||||||
|
|
||||||
|
statPersonne: any = {
|
||||||
|
nbrePersonnePhysique: 10,
|
||||||
|
nbrePersonneMorale: 5,
|
||||||
|
nbrePersonneInformel: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
private crudService: CrudService,
|
||||||
|
private modal: NzModalService,
|
||||||
|
private message: NzMessageService,
|
||||||
|
private globalService: GlobalService,
|
||||||
|
private http: HttpClient,
|
||||||
|
private tokenStorage: TokenStorage,
|
||||||
|
) {
|
||||||
|
this.globalService.getLodingSuccess().subscribe({
|
||||||
|
next: (data: boolean) => {
|
||||||
|
this.isActionInProgress = data;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isActionInProgress = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
|
||||||
|
const token = this.tokenStorage.getToken() != null ? this.tokenStorage.getToken() : '';
|
||||||
|
const helper = new JwtHelperService();
|
||||||
|
const decodeToken = helper.decodeToken(token ? token : '');
|
||||||
|
this.user = decodeToken?.user;
|
||||||
|
console.log(this.user);
|
||||||
|
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
|
||||||
|
if(this.isRoles(['ROLE_ADMIN'])) {
|
||||||
|
const resultStructure: any = await firstValueFrom(this.crudService.getAll('statistique/user/enquete-par-structure'));
|
||||||
|
console.log('resultStructure ===> ', resultStructure);
|
||||||
|
if(resultStructure && resultStructure.object) {
|
||||||
|
this.structureEnqueteList = resultStructure.object;
|
||||||
|
}
|
||||||
|
|
||||||
|
const communes = await firstValueFrom(this.http.get<any>(`${environment.backend}/commune/all`));
|
||||||
|
|
||||||
|
console.log('decoupages ===> ', communes);
|
||||||
|
|
||||||
|
if (communes && communes.object.length > 0) {
|
||||||
|
this.communeList = communes?.object;
|
||||||
|
this.commune = this.communeList[0];
|
||||||
|
this.filterArrondissementByCommune(this.communeList[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.isRoles(['ROLE_SUPERVISEUR', 'ROLE_DIRECTEUR', 'ROLE_ENQUETEUR'])) {
|
||||||
|
const resultBlocs: any = await firstValueFrom(this.crudService.getAll('statistique/user/enquete-par-bloc'));
|
||||||
|
console.log('resultBlocs ===> ', resultBlocs);
|
||||||
|
this.blocEnqueteList = resultBlocs.object;
|
||||||
|
const compareBlocFn = (a: any, b: any) => (a.id < b.id ? 0 : -1);
|
||||||
|
this.blocEnqueteList.sort(compareBlocFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: any = await firstValueFrom(this.crudService.getAll('statistique/user/enquete-par-statut'));
|
||||||
|
console.log('enquete ===> ', result);
|
||||||
|
|
||||||
|
if(result && result.object.length > 0) {
|
||||||
|
this.nombreValide = result.object.find((element: any) => element.statutEnquete == 'VALIDE');
|
||||||
|
this.nombreRejete = result.object.find((element: any) => element.statutEnquete == 'REJETE');
|
||||||
|
this.nombreSynchronise = result.object.find((element: any) => element.statutEnquete == 'FINALISE');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartOptions = {
|
||||||
|
series: [(this.nombreValide ? this.nombreValide.nombre : 0), (this.nombreSynchronise ? this.nombreSynchronise.nombre : 0), (this.nombreRejete ? this.nombreRejete.nombre : 0)],
|
||||||
|
chart: {
|
||||||
|
width: '100%',
|
||||||
|
type: "pie"
|
||||||
|
},
|
||||||
|
labels: ["Validées", "Finalisées", "Rejetées"],
|
||||||
|
responsive: [
|
||||||
|
{
|
||||||
|
breakpoint: 480,
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
width: 400
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: "bottom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chartPersonneOptions = {
|
||||||
|
series: [(this.statPersonne.nbrePersonnePhysique), (this.statPersonne.nbrePersonneMorale), (this.statPersonne.nbrePersonneInformel)],
|
||||||
|
chart: {
|
||||||
|
width: '100%',
|
||||||
|
type: "pie"
|
||||||
|
},
|
||||||
|
labels: ["Personne physique", "Personne morale", "Groupe informel"],
|
||||||
|
responsive: [
|
||||||
|
{
|
||||||
|
breakpoint: 480,
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
width: 300
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: "bottom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
compareFn = (o1: any, o2: any) => (o1 && o2 ? o1.id === o2.id : o1 === o2);
|
||||||
|
|
||||||
|
async filterArrondissementByCommune(event: any): Promise<void> {
|
||||||
|
this.commune = event;
|
||||||
|
if(this.commune && this.commune != null) {
|
||||||
|
this.globalService.setLodingSuccess(true);
|
||||||
|
const result: any = await firstValueFrom(this.crudService.getAll('statistique/enquete-par-arrondissement/'+this.commune.id));
|
||||||
|
console.log('arrondissement statistique ===> ', result);
|
||||||
|
this.arrondissementEnqueteList = result.object;
|
||||||
|
const compareBlocFn = (a: any, b: any) => (a.id < b.id ? 0 : -1);
|
||||||
|
this.arrondissementEnqueteList.sort(compareBlocFn);
|
||||||
|
this.globalService.setLodingSuccess(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isRoles(params: any[]): boolean {
|
||||||
|
if(this.user != null) {
|
||||||
|
return params.indexOf(this.user.roles[0]?.nom) > -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notIsRoles(params: any[]): boolean {
|
||||||
|
if(this.user != null) {
|
||||||
|
return params.indexOf(this.user.roles[0]?.nom) == -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
27
src/app/office/dashbord/dashbord.module.ts
Normal file
27
src/app/office/dashbord/dashbord.module.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { DashbordRoutingModule } from './dashbord-routing.module';
|
||||||
|
import { DashbordComponent } from './dashbord.component';
|
||||||
|
import { SharedModule } from 'src/app/shared/shared.module';
|
||||||
|
import { NgApexchartsModule } from 'ng-apexcharts';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
DashbordComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
DashbordRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
|
||||||
|
SharedModule,
|
||||||
|
NgApexchartsModule,
|
||||||
|
],
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
|
|
||||||
|
})
|
||||||
|
export class DashbordModule { }
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user