10/05/2026 - 12h30

branche_01062025
Cherif 2026-05-10 12:32:58 +02:00
parent 618923b402
commit 61464773f5
14 changed files with 3732 additions and 103 deletions

View File

@ -70,6 +70,7 @@ import {
} from '@mui/x-data-grid';
import Module_Alert_Confirmation from "./Module_Alert_Confirmation";
import Module_Relance_Client_Facture from "./Module_Relance_Client_Facture";
function PaperComponent(props) {
return (
@ -277,7 +278,7 @@ const AddParnerClient = (props) => {
{ field: 'id', headerName: 'id', hide: true, disableExport: true },
{ field: 'invoice_id', headerName: 'invoice_id', hide: true, disableExport: true },
{ field: 'paiement_date', headerName: 'Date Paiement', width: 150, hide: false, editable: false },
{ field: 'paiement_ref', headerName: 'paiement_ref', hide: false, minWidth: 200, flex: 1, editable: false, renderCell: (params) => <ExpandableCell_50 {...params} />, align: 'center', headerAlign: "center"},
{ field: 'paiement_ref', headerName: 'paiement_ref', hide: false, minWidth: 200, flex: 1, editable: false, renderCell: (params) => <ExpandableCell_50 {...params} />, align: 'center', headerAlign: "center" },
{
field: 'paiement_amount', headerName: 'Montant TTC', width: 150, hide: false, editable: false, align: 'right', headerAlign: "right",
renderCell: (params) => <ExpandableCell_String_to_monetaire {...params} />,
@ -6508,7 +6509,7 @@ const AddParnerClient = (props) => {
</div>
ICIII FAIRE UN HISTOGRAMME DES PAIEMENT SUR LES 6 DERNIMER MOIS
ICIII FAIRE UN HISTOGRAMME DES PAIEMENT SUR LES 6 DERNIMER MOIS
</div>
@ -6516,7 +6517,10 @@ ICIII FAIRE UN HISTOGRAMME DES PAIEMENT SUR LES 6 DERNIMER MOIS
{String(submenu).trim() === "submenu_relance" && <div className="training_data">
submenu_relance
submenu_relance <br />
<Module_Relance_Client_Facture
client_id={props.client_id} />
</div>}
<div className="div_row" style={{

View File

@ -251,6 +251,38 @@ const ButtonHilight = () => {
});
}
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
@ -259,6 +291,8 @@ const ButtonHilight = () => {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setpartCookie("tokenmysypart", "", { path: '/' });
setpartCookie(acces_right_token_name, "", { path: '/' });
removeCookie(acces_right_token_name, { path: '/' });
@ -277,6 +311,9 @@ const ButtonHilight = () => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}

View File

@ -165,14 +165,47 @@ const Header = () => {
});
}
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
if (typeof (stored_part) != "undefined") {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setpartCookie("tokenmysypart", "", { path: '/' });
setpartCookie(acces_right_token_name, "", { path: '/' });
removeCookie(acces_right_token_name, { path: '/' });
@ -191,6 +224,9 @@ const Header = () => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}

View File

@ -1323,7 +1323,7 @@ const Jmjformation_Catalogue_Public = (props) => {
myurl = process.env.REACT_APP_API_URL + "myclass/api/get_all_class_Given_partner_owner_recid_No_Login/";
form.append("subdomain", "colasrail");
// form.append("subdomain", "nousformons");
// form.append("subdomain", "jmjformation");
// form.append("subdomain", "jmjformation");
//console.log(" ######### laaaaaaaaaaaaa subdomain = ", window.location.host.split(".")[0])
@ -1944,13 +1944,48 @@ const Jmjformation_Catalogue_Public = (props) => {
}
}
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
if (typeof (stored_part) != "undefined") {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setpartCookie("tokenmysypart", "", { path: '/' });
setpartCookie(acces_right_token_name, "", { path: '/' });
@ -1959,6 +1994,9 @@ const Jmjformation_Catalogue_Public = (props) => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}
@ -2467,7 +2505,7 @@ const Jmjformation_Catalogue_Public = (props) => {
form.append("subdomain", "colasrail");
// form.append("subdomain", "nousformons");
// form.append("subdomain", "jmjformation");
// form.append("subdomain", "jmjformation");
var myurl = process.env.REACT_APP_API_URL + "myclass/api/Get_Partner_Data_From_Subdomain/";
@ -4368,9 +4406,9 @@ const Jmjformation_Catalogue_Public = (props) => {
<div className="jmj_block_milieu_droite">
<div className="rech_middlebox" id="rech_middlebox" style={{ width: '100%' }}>
<div>
<h3 style={{ fontFamily: 'DM Sans', color: '#0A043C' }}
className="mobile_block_nos_formation"> Nos Formations </h3></div>
<div>
<h3 style={{ fontFamily: 'DM Sans', color: '#0A043C' }}
className="mobile_block_nos_formation"> Nos Formations </h3></div>
<div style={{
display: 'flex', flexDirection: 'row', justifyContent: 'center', width: '100%', flexWrap: 'wrap',

View File

@ -0,0 +1,679 @@
import React, { useRef, useState, useEffect } from "react";
import TextField from '@mui/material/TextField';
import 'react-confirm-alert/src/react-confirm-alert.css'; // Import css
import Button from '@mui/material/Button';
import axios from "axios";
import { getCookie, setCookie } from 'react-use-cookie';
import { useHistory } from "react-router-dom";
import { DataGrid, GridToolbar, frFR } from '@mui/x-data-grid';
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import 'react-tooltip/dist/react-tooltip.css'
import Popup from 'reactjs-popup';
import 'reactjs-popup/dist/index.css';
import "react-datepicker/dist/react-datepicker.css";
import 'moment/locale/fr';
import Box from '@mui/material/Box';
import * as XLSX from 'xlsx';
import {
GridToolbarContainer, GridToolbarExport, GridToolbarColumnsButton,
GridToolbarFilterButton, GridToolbarDensitySelector, GridToolbarExportContainer, useGridApiContext,
gridFilteredSortedRowIdsSelector,
gridVisibleColumnFieldsSelector,
} from '@mui/x-data-grid';
import MenuItem from '@mui/material/MenuItem';
import img_loading_spin from "./../mysy_img/mysy_spin_loading.gif";
import Autocomplete from '@mui/material/Autocomplete';
import { gridClasses } from '@mui/x-data-grid';
import Link from '@mui/material/Link';
import { PiDotsThree } from "react-icons/pi";
import { CiTrash } from "react-icons/ci";
import { IoAddCircleOutline } from "react-icons/io5";
import CancelPresentationIcon from '@mui/icons-material/CancelPresentation';
import { makeStyles } from "@material-ui/core/styles";
import Paper from '@material-ui/core/Paper';
import Draggable from 'react-draggable';
import { IconButton } from '@material-ui/core';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import 'react-tooltip/dist/react-tooltip.css'
import TextareaAutosize from '@mui/material/TextareaAutosize';
import Module_Alert_Confirmation from "./Module_Alert_Confirmation";
const useStyles = makeStyles((theme) => ({
dialog: {
position: 'absolute',
// left: 10,
top: 400
},
paper: {
overflowY: 'unset',
},
customizedButton: {
position: 'absolute',
left: '98%',
top: '-3%',
backgroundColor: 'lightgray',
color: 'gray',
},
}));
function PaperComponent(props) {
return (
<Draggable
handle="#draggable-dialog-title"
cancel={'[class*="MuiDialogContent-root"]'}
>
<Paper {...props} />
</Draggable>
);
}
const Module_Relance_Client_Facture = (props) => {
const classes = useStyles();
const history = useHistory();
const [submenu, setsubmenu] = useState("");
const [rowss, setRows] = useState([]);
var date_today_90j = new Date();
date_today_90j.setDate(date_today_90j.getDate() + 90);
const [mouse_x, setmouse_x] = useState("");
const [mouse_y, setmouse_y] = useState("");
const [p_filtre1, setp_filtre1] = useState("Inscrit ");
const [p_filtre1_value, setp_filtre1_value] = useState("");
const [p_filtre2, setp_filtre2] = useState("Type de groupe");
const [p_filtre2_value, setp_filtre2_value] = useState("");
const [p_filtre3, setp_filtre3] = useState("");
const [p_filtre3_value, setp_filtre3_value] = useState("");
const [p_filtre4, setp_filtre4] = useState("");
const [p_filtre4_value, setp_filtre4_value] = useState("");
const [selectionModel, setSelectionModel] = React.useState([]);
function ExpandableCell_50({ value }) {
const [expanded, setExpanded] = React.useState(false);
return (
<nav>
{value && <div style={{ paddingLeft: "5px", paddingRight: "15px", "display": "block", wordBreak: "break-all", paddingTop: '5px' }}>
{expanded ? value : String(value).slice(0, 50)}&nbsp;
{value.length > 50 && (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<Link
type="button"
component="button"
sx={{ fontSize: 'inherit' }}
onClick={() => setExpanded(!expanded)}
>
{expanded ? <nav> <PiDotsThree />Réduire</nav> : <nav> <PiDotsThree />Voir</nav>}
</Link>
)}
</div>}
{!value && <div style={{ paddingLeft: "5px", paddingRight: "15px", "display": "block", wordBreak: "break-all" }}>
&nbsp;
</div>
}
</nav>
);
}
function Display_Colunm_text({ value }) {
return (
<div style={{ paddingLeft: "5px", paddingRight: "10px", "display": "block", wordBreak: "break-all" }}>
{value}&nbsp;
</div>
);
}
function ExpandableCell_String_to_monetaire({ value }) {
const [expanded, setExpanded] = React.useState(false);
const number = parseFloat(value);
var new_value = ""
if (isNaN(number)) {
new_value = "—"
} else {
new_value = new Intl.NumberFormat('fr-FR', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(number);
}
return (
<nav>
{value && <div style={{ paddingLeft: "5px", paddingRight: "15px", "display": "block", wordBreak: "break-all", paddingTop: '5px' }}>
{new_value}
</div>}
{!value && <div style={{ paddingLeft: "5px", paddingRight: "15px", "display": "block", wordBreak: "break-all" }}>
&nbsp;
</div>
}
</nav>
);
}
function local_format_monetaire(local_amount) {
return new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR',
}).format(Number(local_amount));
}
const [selected_id, setselected_id] = useState("");
const [gridline_id, setgridline_id] = useState("");
const [isLoading, setLoading] = useState();
const columns_relances = [
{ field: '_id', headerName: '_id', hide: true, disableExport: true, },
{ field: 'id', headerName: 'id', hide: true, disableExport: true },
{ field: 'client_id', headerName: 'client_id', hide: true, disableExport: true },
{ field: 'relance_date', headerName: 'Date Relance', width: 150, hide: false, editable: false },
{ field: 'relance_level', headerName: 'relance_level', hide: false, minWidth: 200, flex: 1, editable: false, renderCell: (params) => <ExpandableCell_50 {...params} />, align: 'center', headerAlign: "center" },
{
field: 'total_reste_to_paye', headerName: 'Montant Rel.', width: 150, hide: false, editable: false, align: 'right', headerAlign: "right",
renderCell: (params) => <ExpandableCell_String_to_monetaire {...params} />,
},
]
const [New_Get_List_Relance_Client_Facture_result, setNew_Get_List_Relance_Client_Facture_result] = useState([]);
const [Get_List_Relance_Client_Facture_api, setGet_List_Relance_Client_Facture_api] = useState();
const [Get_List_Relance_Client_Facture_message, setGet_List_Relance_Client_Facture_message] = useState();
const [Get_List_Relance_Client_Facture_result, setGet_List_Relance_Client_Facture_result] = useState([]);
function Get_List_Relance_Client_Facture(event) {
var form = new FormData();
const stored_cookie = getCookie('tokenmysypart');
const related_collection = "partner_client"
form.append("token", stored_cookie);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/Get_List_Relance_with_filter/";
axios.post(myurl, form).then(res => {
//console.log(" In Get_List_Relance_Client_Facture res.data.status = " + res.data.status);
//console.log(" In Get_List_Relance_Client_Facture res.data.message r_class = " + res.data.message);
if (String(res.data.status) === String("true")) {
setGet_List_Relance_Client_Facture_api("true");
setGet_List_Relance_Client_Facture_result(res.data.message);
var new_data2 = [];
const new_data = res.data.message.map((x) => {
new_data2.push(XMLHttpRequest);
});
if (new_data2.length > 0)
setNew_Get_List_Relance_Client_Facture_result(new_data2);
else
setNew_Get_List_Relance_Client_Facture_result([]);
}
else if (String(res.data.status) === String("Err_Connexion")) {
alert('Erreur: ' + res.data.message);
history.push("/Connexion");
}
else {
setGet_List_Relance_Client_Facture_api("false");
setGet_List_Relance_Client_Facture_message(res.data.message);
//alert(res.data.message);
setdisplay_alert_mysy("1");
setalert_message(res.data.message);
setalert_type("error");
}
}).catch((error) => {
console.warn('Not good man :( Get_List_Relance_Client_Facture = ', error);
setGet_List_Relance_Client_Facture_api("false");
//alert(" Impossible de récupérer les types de clients");
setdisplay_alert_mysy("1");
setalert_message(" Impossible de récupérer les relances");
setalert_type("error");
})
}
useEffect(() => {
Get_List_Relance_Client_Facture();
}, []);
// DEBUT EXPORT EXCEL DATAGRID
function CustomToolbar(props) {
return (
<GridToolbarContainer {...props}>
<GridToolbarColumnsButton />
<GridToolbarFilterButton />
<GridToolbarDensitySelector
slotProps={{ tooltip: { title: 'Change density' } }}
/>
<Box sx={{ flexGrow: 1 }} />
<ExportButton data_colums={props.data_colums} />
</GridToolbarContainer>
);
}
function getExcelData(apiRef) {
// Select rows and columns
const filteredSortedRowIds = gridFilteredSortedRowIdsSelector(apiRef);
const visibleColumnsField = gridVisibleColumnFieldsSelector(apiRef).filter((data) => (data) !== "__check__");
// Format the data. Here we only keep the value
const data = filteredSortedRowIds.map((id) => {
const row = {};
visibleColumnsField.forEach((field) => {
var val = apiRef.current.getCellParams(id, field).value;
if (String(field) !== "__check__")
row[field] = apiRef.current.getCellParams(id, field).value;
});
return row;
});
return data;
}
function ExportButton(props) {
return (
<GridToolbarExportContainer {...props}>
<ExportMenuItem data_colums={props.data_colums} />
</GridToolbarExportContainer>
);
}
function handleExport(apiRef, data_colums) {
const data = getExcelData(apiRef);
const visibleColumnsField = gridVisibleColumnFieldsSelector(apiRef).filter((data) => (data) !== "__check__");
const local_config = {
keys: visibleColumnsField,
columnNames: data_colums.filter((mydata) => (mydata).disableExport !== true).map(function (mydata) {
if (mydata.headerName && visibleColumnsField.includes(mydata.field)) {
if (mydata.headerName) {
return mydata.headerName;
}
}
}),
fileName: 'data_' + String(new Date().toJSON().slice(0, 10)).replaceAll("-", "_") + '.xlsx',
sheetName: 'Export_' + String(new Date().toJSON().slice(0, 10)).replaceAll("-", "_"),
};
local_config.columnNames = local_config.columnNames.filter(function (element) {
return element !== undefined;
});
const rows = data.map((row) => {
const mRow = {};
for (const key of local_config.keys) {
mRow[key] = row[key];
}
return mRow;
});
const worksheet = XLSX.utils.json_to_sheet(rows);
XLSX.utils.sheet_add_aoa(worksheet, [[...local_config.columnNames]], {
origin: 'A1',
});
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, local_config.sheetName);
XLSX.writeFile(workbook, local_config.fileName, { compression: true });
}
function ExportMenuItem(props) {
const apiRef = useGridApiContext();
const { hideMenu } = props;
return (
<MenuItem
onClick={() => {
handleExport(apiRef, props.data_colums);
// Hide the export menu after the export
hideMenu?.();
}}
>
Export Excel
</MenuItem>
);
}
// FIN EXPORT EXCEL DATAGRID
const [display_alert_mysy, setdisplay_alert_mysy] = useState("");
const [alert_message, setalert_message] = useState("");
const [alert_type, setalert_type] = useState("");
function clear_alert_message() {
setalert_message("");
}
function clear_alert_type() {
setalert_type("");
}
function clear_display_alert_mysy() {
setdisplay_alert_mysy("");
}
const [selectionModel_relance, setselectionModel_relance] = React.useState([]);
return (
<div className="module_relance_client_facture">
{isLoading && <div className="loader-container">
<div className="mysy_spinner"> <img src={img_loading_spin} /> </div>
</div>}
{/*** Affichage des messages d'alerte*/}
{display_alert_mysy && String(display_alert_mysy) === "1" &&
<Module_Alert_Confirmation alert_message={alert_message}
alert_type={alert_type}
clear_alert_message={clear_alert_message}
clear_alert_type={clear_alert_type}
clear_display_alert_mysy={clear_display_alert_mysy}
/>
}
{/*** FIN Affichage des messages d'alerte*/}
<div> Module de gestion des relance client </div>
Total Relance <br />
<div className="div_row" style={{ "border": "None", paddingRight: '10px' }}>
<div className="session_data">
{/*rowss_total_data && rowss_total_data.length > 0 && rowss_total_data.map((val) => (<nav style={{ "textAlign": 'right', 'marginRight': '15px', "fontWeight": "600", "fontSize": "15px", "paddingTop": "2px" }}> {parseFloat(String(val.somme_header_ca_ht)).toLocaleString()} (€) HT/ {val.nb_header_invoice} Factures </nav>))*/}
<div style={{ "border": "None" }}>
<div style={{ height: 550, width: '100%', paddingRight: '5px' }}>
&nbsp;
<Box
sx={{
height: 500,
width: '100%',
paddingRight: '1px',
// Gestion des cellule "statut"
'& .cell--status--encours': {
backgroundColor: '#E6F7C8',
color: '#1a3e72',
fontWeight: 'light',
},
'& .cell--status--traite': {
backgroundColor: '#E6CEAA',
color: '#F9E79F',
fontWeight: 'light',
},
/*"& .MuiDataGrid-virtualScrollerRenderZone": {
"& .MuiDataGrid-row": {
"&:nth-child(2n)": { backgroundColor: "rgba(235, 235, 235, .7)" }
}
},*/
"& .MuiDataGrid-columnHeaders": {
backgroundColor: "#dfe8f2",
color: "#0a2a4d",
fontSize: '14px',
fontFamily: 'DM Sans',
},
"& .MuiDataGrid-renderingZone, .MuiDataGrid-root .MuiDataGrid-row": {
width: '100%',
},
'& .line--statut--selected': {
backgroundColor: '#e9f7f1',
color: '#0a2a4d',
},
'& .line--statut--pair': {
backgroundColor: 'rgba(235, 235, 235, .7)',
color: '#0a2a4d',
},
'& .line--statut--impair': {
backgroundColor: '#FFFFFF',
color: '#0a2a4d',
},
'& .line--statut--payed': {
backgroundColor: '#EAFAF1',
color: '#239B56',
fontWeight: '700'
},
}}
>
<DataGrid
components={{
Toolbar: CustomToolbar
}}
componentsProps={{ toolbar: { data_colums: columns_relances } }}
checkboxSelection
disableSelectionOnClick
onSelectionModelChange={(newSelectionModel) => {
setselectionModel_relance(newSelectionModel);
/*if (newSelectionModel.length === 1)
handleClick_edit_invoice_From_Line(newSelectionModel);*/
if (newSelectionModel.length !== 1) {
//clean_order_detail_fields();
//setsubmenu();
// setdisplay_detail_invoice();
}
}}
onRowDoubleClick={(newSelectionModel) => {
}
}
selectionModel={selectionModel_relance}
localeText={frFR.components.MuiDataGrid.defaultProps.localeText}
rows={Get_List_Relance_Client_Facture_result.map((item, index) => (
{
id: index,
_id: JSON.parse(item)._id,
clien_id: JSON.parse(item).clien_id,
tab_invoice: JSON.parse(item).tab_invoice,
relance_date: JSON.parse(item).relance_date,
relance_level: JSON.parse(item).relance_level,
total_reste_to_paye: JSON.parse(item).total_reste_to_paye,
}
))}
columns={columns_relances}
pageSize={10}
className="datagridclass"
rowsPerPageOptions={[10]}
//disableSelectionOnClick
/* components={{
Toolbar: GridToolbar,
}}*/
//sx={datagridSx}
getCellClassName={(params) => {
}}
getRowClassName={(params) => {
// Pour la gestion de la couleur de zone double cliquée
if (String(params.row.id) === String(gridline_id)) {
return 'line--statut--selected';
}
else if (parseInt(String(params.row.id)) % 2 === 0) {
return 'line--statut--pair';
}
else if (parseInt(String(params.row.id)) % 2 !== 0) {
return 'line--statut--impair';
}
}}
/>
</Box>
<br />
</div>
</div>
</div>
ICIII FAIRE UN HISTOGRAMME DES PAIEMENT SUR LES 6 DERNIMER MOIS
</div>
<style>{local_css}</style>
</div >
);
}
export default Module_Relance_Client_Facture;
const local_css = `
:root {
--bg: #f6f8fb;
--card: #ffffff;
--text: #0f172a;
--muted: #64748b;
--border: #e5e7eb;
--shadow: 0 10px 25px rgba(2, 6, 23, .06);
--green-700: #0f6b53;
--green-600: #167a5e;
--green-100: #e9f7f1;
--blue-600: #2563eb;
--radius: 14px;
--radius-sm: 10px;
}
.Grid_2_col{
margin-top: 12px;
display:grid;
grid-template-columns: 1.2fr 1fr;
gap: 14px;
}
.Grid_2_col_sub_colone {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 14px;
box-shadow: 0 6px 18px rgba(2,6,23,.04);
padding: 0px;
display: flex;
flex-direction: column;
gap: 14px;
}
.Grid_1_col{
margin-top: 12px;
display:grid;
grid-template-columns: 1fr;
gap: 14px;
padding:5px;
}
.Grid_2_col_sub_colone_header {
background: #dbeafe;
border-top-right-radius: 10px;
border-top-left-radius: 10px;
text-align: center;
font-family: DM Sans;
font-weight: 700;
height: 2rem;
padding-top: 5px;
font-size: 15px;
}
.Grid_2_col_sub_colone_bottom_menu{
}
.card__header{
background: #e2e8f0;
font-size: small;
text-align: center;
}
.card__title{
display:flex;
align-items:center;
gap: 10px;
}
.card__icon{
width: 34px;
height: 34px;
display:grid;
place-items:center;
border-radius: 12px;
background:#eff6ff;
border:1px solid #dbeafe;
}
.card__header h2{
margin:0;
font-size: 15px;
letter-spacing: -0.01em;
}
.card__actions{ display:flex; align-items:center; gap:8px; }
.block_detail_action{
width: 100%;
float: left;
background: #fafafa;
padding: 10px;
border: unset;
border-radius: 10px;
}
`;

View File

@ -309,7 +309,7 @@ function New_C_Sign_In_2025() {
//setCookie("tokenmysypart", result['message']);
console.log("## last_visite_menu = ", last_visite_menu);
// console.log("## last_visite_menu = ", last_visite_menu);
if (last_visite_menu && last_visite_menu.length > 3)
history.push(last_visite_menu);

View File

@ -1293,6 +1293,38 @@ const Partner = (props) => {
}
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
// Cette fontion desactive tous les menu header et reactive just le menu_header concerné
function inactive_active_menu_header(current_menu_header) {
@ -1651,7 +1683,7 @@ const Partner = (props) => {
function mes_suivi_paiement_func(event) {
var security_retval = check_user_acces_right("facture_client", "read");
if (security_retval === 1) {
console.log(" OKKK ")
// console.log(" OKKK ")
setmenu("suivi_paiement");
setapiexcelimportmessage();
setformation_file_name();
@ -2831,6 +2863,8 @@ const Partner = (props) => {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setCookie_part("tokenmysypart", "", { path: '/' });
setCookie_part(acces_right_token_name, "", { path: '/' });
removeCookie_part(acces_right_token_name, { path: '/' });
@ -2849,6 +2883,9 @@ const Partner = (props) => {
}
// setsomeoneconnected("0");
setDialog_deconnexion_open(false);
history.push("/recherche-formation");
}

View File

@ -4,7 +4,7 @@ import { getCookie, setCookie } from 'react-use-cookie';
import Box from '@mui/material/Box';
import InputAdornment from '@mui/material/InputAdornment';
import TextField from '@mui/material/TextField';
import axios from "axios";
import EmailIcon from '@mui/icons-material/Email'
import { Button, Form, FormGroup, Label } from "reactstrap";
import Visibility from '@mui/icons-material/Visibility';
@ -629,7 +629,158 @@ const Partner_Securite_Data = (props) => {
};
const [p_detail_code_banque, setp_detail_code_banque] = useState("");
const [p_detail_code_guichet, setp_detail_code_guichet] = useState("");
const [p_detail_compte_bancaire, setp_detail_compte_bancaire] = useState("");
const [p_detail_code_cle, setp_detail_code_cle] = useState("");
const [p_detail_code_iban, setp_detail_code_iban] = useState("");
const [p_detail_code_bic, setp_detail_code_bic] = useState("");
const [bank_data_changed, setbank_data_changed] = useState("");
const [bank_data_edit, setbank_data_edit] = useState("");
const [New_Get_Partner_Bank_Data_result, setNew_Get_Partner_Bank_Data_result] = useState([]);
const [Get_Partner_Bank_Data_api, setGet_Partner_Bank_Data_api] = useState();
const [Get_Partner_Bank_Data_message, setGet_Partner_Bank_Data_message] = useState();
const [Get_Partner_Bank_Data_result, setGet_Partner_Bank_Data_result] = useState([]);
function Get_Partner_Bank_Data(event) {
var form = new FormData();
const stored_cookie = getCookie('tokenmysypart');
form.append("token", stored_cookie);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/Get_Partner_bank_data/";
setLoading(true);
axios.post(myurl, form).then(res => {
setLoading(false);
if (String(res.data.status) === String("true")) {
//console.log(" In Get_Partner_Bank_Data res.data.status = " + res.data.status);
console.log(" In Get_Partner_Bank_Data res.data.message r_class = " + res.data.message);
setGet_Partner_Bank_Data_api("true");
setGet_Partner_Bank_Data_result(res.data.message);
if (res.data.message.length > 0) {
var mylocal_data = JSON.parse(res.data.message);
if (mylocal_data.code_banque)
setp_detail_code_banque(mylocal_data.code_banque);
else
setp_detail_code_banque("");
if (mylocal_data.code_guichet)
setp_detail_code_guichet(mylocal_data.code_guichet);
else
setp_detail_code_guichet("");
if (mylocal_data.compte_bancaire)
setp_detail_compte_bancaire(mylocal_data.compte_bancaire);
else
setp_detail_compte_bancaire("");
if (mylocal_data.code_cle)
setp_detail_code_cle(mylocal_data.code_cle);
else
setp_detail_code_cle("");
if (mylocal_data.code_iban)
setp_detail_code_iban(mylocal_data.code_iban);
else
setp_detail_code_iban("");
if (mylocal_data.code_bic)
setp_detail_code_bic(mylocal_data.code_bic);
else
setp_detail_code_bic("");
}
desablesecurty_bank_fields();
}
else {
setGet_Partner_Bank_Data_api("false");
setGet_Partner_Bank_Data_message(res.data.message);
// alert(res.data.message);
setdisplay_alert_mysy("1");
setalert_message(res.data.message);
setalert_type("error");
}
}).catch((error) => {
setLoading(false);
console.warn('Not good man :( Get_Partner_Bank_Data = ', error);
setGet_Partner_Bank_Data_api("false");
alert(" Impossible de recuperer les données bancaires");
//setmyApimyApiMessage("")
})
}
const [Update_Partner_Bank_Data_api, setUpdate_Partner_Bank_Data_api] = useState();
const [Update_Partner_Bank_Data_message, setUpdate_Partner_Bank_Data_message] = useState();
const [Update_Partner_Bank_Data_result, setUpdate_Partner_Bank_Data_result] = useState([]);
function Update_Partner_Bank_Data(event) {
var form = new FormData();
const stored_cookie = getCookie('tokenmysypart');
form.append("token", stored_cookie);
form.append("code_banque", p_detail_code_banque);
form.append("code_guichet", p_detail_code_guichet);
form.append("compte_bancaire", p_detail_compte_bancaire);
form.append("code_cle", p_detail_code_cle);
form.append("code_iban", p_detail_code_iban);
form.append("code_bic", p_detail_code_bic);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/update_partner_bank_data/";
setLoading(true);
axios.post(myurl, form).then(res => {
setLoading(false);
if (String(res.data.status) === String("true")) {
//console.log(" In Get_Partner_Bank_Data res.data.status = " + res.data.status);
// console.log(" In Get_Partner_Bank_Data res.data.message r_class = " + res.data.message);
setUpdate_Partner_Bank_Data_api("true");
setUpdate_Partner_Bank_Data_result(res.data.message);
setdisplay_alert_mysy("1");
setalert_message(res.data.message);
setalert_type("succes");
desablesecurty_bank_fields();
}
else {
setUpdate_Partner_Bank_Data_api("false");
setUpdate_Partner_Bank_Data_message(res.data.message);
// alert(res.data.message);
setdisplay_alert_mysy("1");
setalert_message(res.data.message);
setalert_type("error");
}
}).catch((error) => {
setLoading(false);
console.warn('Not good man :( Get_Partner_Bank_Data = ', error);
setUpdate_Partner_Bank_Data_api("false");
alert(" Impossible de mettre à jour les données bancaires");
//setmyApimyApiMessage("")
})
}
useEffect(() => {
Get_Partner_Bank_Data();
desablesecurtymailfields();
desablesecurtypwdfields();
@ -637,6 +788,45 @@ const Partner_Securite_Data = (props) => {
}, [])
async function enablesecurty_bank_fields() {
setbank_data_edit("1");
await sleep(5);
var liste_champs = ['code_banque', 'code_guichet', 'compte_bancaire', 'code_cle', 'code_iban', 'code_bic']
for (let i = 0; i < liste_champs.length; i++) {
if (document.getElementsByName(String(liste_champs[i])) && document.getElementsByName(String(liste_champs[i]))[0]) {
document.getElementsByName(String(liste_champs[i]))[0].disabled = false;
document.getElementsByName(String(liste_champs[i]))[0].style.backgroundColor = "#FFFFFF";
}
}
}
async function desablesecurty_bank_fields() {
setbank_data_edit("0");
setbank_data_changed("0");
await sleep(5);
var liste_champs = ['code_banque', 'code_guichet', 'compte_bancaire', 'code_cle', 'code_iban', 'code_bic']
for (let i = 0; i < liste_champs.length; i++) {
if (document.getElementsByName(String(liste_champs[i])) && document.getElementsByName(String(liste_champs[i]))[0]) {
document.getElementsByName(String(liste_champs[i]))[0].disabled = true;
document.getElementsByName(String(liste_champs[i]))[0].style.backgroundColor = "#ECEFF1";
}
}
}
const [p_current_pwd, setp_current_pwd] = useState("");
const [p_new_pwd, setp_new_pwd] = useState("");
const [p_new_pwd_conf, setp_new_pwd_conf] = useState("");
@ -1099,22 +1289,14 @@ const Partner_Securite_Data = (props) => {
id="code_banque"
name="code_banque"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '50%' }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_code_banque}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_code_banque(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '50%' }}
/>
<TextField
@ -1122,22 +1304,14 @@ const Partner_Securite_Data = (props) => {
id="code_guichet"
name="code_guichet"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '50%' }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_code_guichet}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_code_guichet(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '50%' }}
/>
<TextField
@ -1145,22 +1319,14 @@ const Partner_Securite_Data = (props) => {
id="compte_bancaire"
name="compte_bancaire"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '65%', float:'left', }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_compte_bancaire}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_compte_bancaire(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '65%', float: 'left', }}
/>
<TextField
@ -1168,22 +1334,14 @@ const Partner_Securite_Data = (props) => {
id="code_cle"
name="code_cle"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '23%', float:'left', }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_code_cle}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_code_cle(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '23%', float: 'left', }}
/>
<TextField
@ -1192,21 +1350,14 @@ const Partner_Securite_Data = (props) => {
name="code_iban"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '90%' }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_code_iban}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_code_iban(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '90%' }}
/>
<TextField
@ -1214,40 +1365,39 @@ const Partner_Securite_Data = (props) => {
id="code_bic"
name="code_bic"
type={'text'}
value={values.current_insert_key}
onChange={handleChange_current_insert_key('current_insert_key')}
sx={{ m: 1, width: '90%' }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickcurrent_insert_key}
edge="end"
>
value={p_detail_code_bic}
</IconButton>
</InputAdornment>
),
onChange={(e) => {
setp_detail_code_bic(e.target.value);
setbank_data_edit("1");
}}
sx={{ m: 1, width: '90%' }}
/>
<div className="div_row22" style={{ "textAlign": "left" }}>
{insert_keyfieldsdesabled == false && <div className="div_row">
{bank_data_edit === "1" && <div className="div_row">
<div className="div_row_gauche">
<Button variant="contained" color="success" className="div_row22 btn_enreg " onClick={UpdatePartnerInsertKey}>Enregistrer</Button>
<Button variant="contained" color="success" className="div_row22 btn_enreg " onClick={Update_Partner_Bank_Data}>Enregistrer</Button>
</div>
<div className="div_row_droite">
<div style={{ width: '100%', textAlign: 'right', paddingRight: '10px' }}> <Button variant="contained" color="success" className="div_row22 btn_modif " onClick={desablesecurtyInsertKeyfields}>Annuler</Button> </div>
<div style={{ width: '100%', textAlign: 'right', paddingRight: '10px' }}> <Button variant="contained" color="success" className="div_row22 btn_modif "
onClick={(e) => {
Get_Partner_Bank_Data();
desablesecurty_bank_fields();
}} >
Annuler</Button> </div>
</div>
</div>
}
{insert_keyfieldsdesabled == true &&
<div style={{ width: '100%', textAlign: 'right', paddingRight: '10px' }}> <Button variant="contained" color="success" className="div_row22 btn_modif" style={{ textAlign: 'center' }} onClick={enablesecurtyInsertKeyfields}>Editer</Button> </div>
{bank_data_edit !== "1" &&
<div style={{ width: '100%', textAlign: 'right', paddingRight: '10px' }}> <Button variant="contained" color="success" className="div_row22 btn_modif" style={{ textAlign: 'center' }} onClick={enablesecurty_bank_fields}>Editer</Button> </div>
}
</div>

View File

@ -3,7 +3,7 @@ import './Header.css';
import moment from "moment";
import { getCookie } from 'react-use-cookie';
import { useHistory } from "react-router-dom";
import axios from "axios";
import Header_MySy_Logo from '../../img_new_design_2025/header_mysy_logo.png';
import Header_User from '../../img_new_design_2025/header_user.png';
import Dialog from '@mui/material/Dialog';
@ -134,13 +134,50 @@ const Header = () => {
setDialog_1_open(false);
};
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
//const stored_cookie = getCookie('tokenmysypart');
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout iii res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
if (typeof (stored_part) != "undefined") {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setpartCookie("tokenmysypart", "", { path: '/' });
setpartCookie(acces_right_token_name, "", { path: '/' });
removeCookie(acces_right_token_name, { path: '/' });
@ -159,6 +196,8 @@ const Header = () => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}
@ -284,7 +323,7 @@ const Header = () => {
</nav>
</div>
{process.env.REACT_APP_ENV !== 'PROD' && <div style={{ background: 'orange', paddingLeft:'20px', paddingRight:'20px' }}> Environnement : {process.env.REACT_APP_ENV}
{process.env.REACT_APP_ENV !== 'PROD' && <div style={{ background: 'orange', paddingLeft: '20px', paddingRight: '20px' }}> Environnement : {process.env.REACT_APP_ENV}
</div>}
<div className="cta-login">

View File

@ -202,6 +202,38 @@ const Header_Client_Intranet = () => {
setDialog_1_open(false);
};
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
@ -209,6 +241,8 @@ const Header_Client_Intranet = () => {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
setpartCookie("tokenmysypart", "", { path: '/' });
setpartCookie(acces_right_token_name, "", { path: '/' });
removeCookie(acces_right_token_name, { path: '/' });
@ -227,6 +261,7 @@ const Header_Client_Intranet = () => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}

View File

@ -252,6 +252,39 @@ const Header_ENT_Student = () => {
setDialog_1_open(false);
};
const [Server_Logout_api, setServer_Logout_api] = useState();
const [Server_Logout_message, setServer_Logout_message] = useState();
const [Server_Logout_result, setServer_Logout_result] = useState([]);
function Server_Logout(local_token) {
var form = new FormData();
form.append("token", local_token);
var myurl = process.env.REACT_APP_API_URL + "myclass/api/partner_logout/";
axios.post(myurl, form).then(res => {
if (String(res.data.status) === String("true")) {
//console.log(" In Server_Logout res.data.status = " + res.data.status);
// console.log(" In Server_Logout res.data.message r_class = " + res.data.message);
setServer_Logout_api("true");
setServer_Logout_result(res.data.message);
}
else {
setServer_Logout_api("false");
setServer_Logout_message(res.data.message);
}
}).catch((error) => {
console.warn('Not good man :( Server_Logout = ', error);
setServer_Logout_api("false");
//alert("Impossible de récuperer la liste des emails");
//setmyApimyApiMessage("")
})
}
function part_logout_confirmation() {
/* si c'est une connexion partner qui est active */
@ -259,6 +292,8 @@ const Header_ENT_Student = () => {
const stored_cookie = getCookie('tokenmysypart');
var acces_right_token_name = stored_cookie + "_uar";
Server_Logout(stored_cookie);
removeCookie(acces_right_token_name,);
@ -279,6 +314,7 @@ const Header_ENT_Student = () => {
}
setsomeoneconnected("0");
setDialog_1_open(false);
history.push("/recherche-formation");
}

View File

@ -1559,7 +1559,7 @@ const New_C_Marketplace_2025 = () => {
axios.post(myurl, form).then(res => {
if (String(res.data.status) != String("false")) {
//console.log(" In test res.data.status = " + res.data.status);
console.log(" In DisplayXfirstClas res.data.message = " + res.data.message);
// console.log(" In DisplayXfirstClas res.data.message = " + res.data.message);
setmyApiResponse("True");
setResult(res.data.message)

File diff suppressed because it is too large Load Diff

View File

@ -223,6 +223,7 @@
@import "./components/module_domaine_competence_pedagogique";
@import "./components/module_gestion_stage";
@import "./components/module_inscrit_suivi_paiement";
@import "./components/module_relance_client_facture";
// New Design 2025