08/05/2026 - 17h
parent
7cfeaefafe
commit
e5ab6f881e
|
|
@ -9355,7 +9355,6 @@ const DisplayPartnerStagiaires = (props) => {
|
|||
})
|
||||
}
|
||||
|
||||
/// iciiii
|
||||
return (
|
||||
<div className="displaypartnerstagiaire">
|
||||
{isLoading && <div className="loader-container">
|
||||
|
|
@ -11460,7 +11459,7 @@ const DisplayPartnerStagiaires = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
</DialogActions>
|
||||
</DialogActions>
|
||||
|
||||
</Dialog>
|
||||
|
||||
|
|
@ -11923,17 +11922,7 @@ const DisplayPartnerStagiaires = (props) => {
|
|||
|
||||
</div>
|
||||
|
||||
{/*String(p_filtre2).length > 2 && <div className='filter_bton_add' >
|
||||
<Tooltip className="tooltip_css" id="my-tooltip03" style={{ "fontSize": "12px" }} />
|
||||
<a data-tooltip-id="my-tooltip03" data-tooltip-html="Ajouter une nouvelle ligne de filtre">
|
||||
<MdAddCircleOutline
|
||||
onClick={(e) => {
|
||||
setp_filtre4("1");
|
||||
setp_filtre4_value("");
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</div>*/}
|
||||
|
||||
|
||||
{String(p_filtre3).length > 2 && <div className='filter_bton_add'>
|
||||
<Tooltip className="tooltip_css" id="my-tooltip05" style={{ "fontSize": "12px" }} />
|
||||
|
|
@ -11947,80 +11936,7 @@ const DisplayPartnerStagiaires = (props) => {
|
|||
</div>
|
||||
}
|
||||
|
||||
{/*p_filtre4 &&
|
||||
<div className="div_row" style={{ "marginBottom": "5px" }}>
|
||||
<div className="div_row_gauche texte_area_filter">
|
||||
|
||||
{New_Option_Filter && New_Option_Filter.length > 0 && <Autocomplete
|
||||
disablePortal
|
||||
name="filtre4"
|
||||
id="filtre4"
|
||||
className="disabled_style"
|
||||
value={New_Option_Filter.filter((data) => (data).value === String(p_filtre4))[0].label}
|
||||
options={New_Option_Filter}
|
||||
onChange={(event, value) => {
|
||||
setp_filtre4_value("");
|
||||
if (value && value.value) {
|
||||
setp_filtre4(value.value);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
renderInput={(params) => <TextField {...params} label="" placeholder="Choisir une option"
|
||||
inputProps={{ ...params.inputProps, style: { fontSize: 12, height: "1.4rem" } }}
|
||||
|
||||
/>
|
||||
}
|
||||
/>}
|
||||
|
||||
|
||||
</div>
|
||||
<div className="div_row_droite texte_area_filter_value" style={{ "marginTop": "0.5rem" }}>
|
||||
{String(p_filtre4).length > 2 &&
|
||||
<TextField
|
||||
sx={{ '& legend': { display: 'none' }, '& fieldset': { top: 0 }, }}
|
||||
required
|
||||
name="filtre4_value"
|
||||
id="filtre4_value"
|
||||
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
disabled={false}
|
||||
className="disabled_style"
|
||||
value={p_filtre4_value}
|
||||
onChange={(e) => setp_filtre2_value(e.target.value)}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<AiFillCloseCircle
|
||||
style={{ 'cursor': "pointer", "color": "orangered" }}
|
||||
onClick={(e) => {
|
||||
setp_filtre4_value("");
|
||||
}} />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
|
||||
/>}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{String(p_filtre2).length > 2 && <div className='filter_bton_add'>
|
||||
<Tooltip className="tooltip_css" id="my-tooltip04" style={{ "fontSize": "12px" }} />
|
||||
<a data-tooltip-id="my-tooltip04" data-tooltip-html="Supprimer cette ligne de filtre">
|
||||
<MdRemoveCircleOutline
|
||||
onClick={(e) => {
|
||||
setp_filtre4("");
|
||||
setp_filtre4_value("");
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
</div>}
|
||||
|
||||
</div>*/
|
||||
}
|
||||
<div className="div_row" ref={myRef_head} id="myRef_head" style={{ "border": "None", "color": "orange", "textAlign": "center" }}>
|
||||
|
||||
<div className="div_row_gauche" style={{ "textAlign": 'left', 'marginLeft': '15px' }}>
|
||||
|
|
|
|||
|
|
@ -373,6 +373,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
function GetAllClass_WOfilter(event) {
|
||||
var form = new FormData();
|
||||
|
||||
|
||||
//form.append("token","K3cw63eKokKSbV4spjQ48SUNnfy80yrXfA");
|
||||
const stored_cookie = getCookie('tokenmysypart');
|
||||
form.append("token", stored_cookie);
|
||||
|
|
@ -1632,7 +1633,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
//console.log('Success:', result['message'], "result['status'] = ", result['status']);
|
||||
|
||||
setactionmass_ftion_val("");
|
||||
if (String(result['status']) === String("true")) {
|
||||
//alert(" OK pour la formation :" + tab_class[i]);
|
||||
}
|
||||
|
|
@ -1669,7 +1670,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
//console.log('Success:', result['message'], "result['status'] = ", result['status']);
|
||||
|
||||
setactionmass_ftion_val("");
|
||||
if (String(result['status']) === String("true")) {
|
||||
//alert(" OK pour la formation :" + tab_class[i]);
|
||||
}
|
||||
|
|
@ -1705,6 +1706,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
//console.log('Success:', result['message'], "result['status'] = ", result['status']);
|
||||
setactionmass_ftion_val("");
|
||||
|
||||
if (String(result['status']) === String("true")) {
|
||||
//alert(" OK pour la formation :" + tab_class[i]);
|
||||
|
|
@ -1742,8 +1744,8 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
)
|
||||
.then((response) => response.json())
|
||||
.then((result) => {
|
||||
//console.log('Success:', result['message'], "result['status'] = ", result['status']);
|
||||
|
||||
// console.log('Success:', result['message'], "result['status'] = ", result['status']);
|
||||
setactionmass_ftion_val("");
|
||||
if (String(result['status']) === String("true")) {
|
||||
alert(String(result['message']))
|
||||
}
|
||||
|
|
@ -2599,6 +2601,8 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
setSelectionModel([]);
|
||||
GetAllClass_new();
|
||||
|
||||
setactionmass_ftion_val("");
|
||||
|
||||
// alert(res.data.message)
|
||||
setdisplay_alert_mysy("1");
|
||||
setalert_message(res.data.message);
|
||||
|
|
@ -2659,7 +2663,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
|
||||
const New_Option = [
|
||||
{ "id": "0", "label": "Publier", "value": "publier" },
|
||||
{ "id": "1", "label": "Depublier", "value": "depublier" },
|
||||
{ "id": "1", "label": "Dépublier", "value": "depublier" },
|
||||
{ "id": "2", "label": "Dupliquer", "value": "dupliquer" },
|
||||
{ "id": "3", "label": "Exporter", "value": "exporter" },
|
||||
{ "id": "4", "label": "Supprimer", "value": "supprimer" },
|
||||
|
|
@ -3036,7 +3040,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
|
||||
{updatetraining && <div>
|
||||
|
||||
<div className='detail_training'>
|
||||
<div className='detail_training'>
|
||||
<TrainingForm mytrainingclass={{
|
||||
'ref': reftrainingtoupdate, 'class_external_code': externalcodetraining_toupdate,
|
||||
'class_id': classidtrainingtoupdate, 'class_internal_url': props.class_internal_url
|
||||
|
|
@ -3904,29 +3908,7 @@ const DistplayPartnerTraningsPage = (props) => {
|
|||
/>
|
||||
|
||||
|
||||
{/* <TextField
|
||||
|
||||
select
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
inputProps={{ min: "0", max: "1", step: "1" }}
|
||||
disabled={false}
|
||||
className="block_en_mass_select"
|
||||
|
||||
value={actionmass_ftion_val}
|
||||
onChange={actionmass_ftion}
|
||||
>
|
||||
<MenuItem value="" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Action </MenuItem>
|
||||
<MenuItem value="publier" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Publier </MenuItem>
|
||||
<MenuItem value="depublier" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Dépublier </MenuItem>
|
||||
<MenuItem value="dupliquer" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Dupliquer </MenuItem>
|
||||
<MenuItem value="supprimer" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Supprimer </MenuItem>
|
||||
<MenuItem value="exporter" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Exporter </MenuItem>
|
||||
|
||||
</TextField>*/}
|
||||
|
||||
{actionmass_ftion_val && <nav className='block_en_mass_bton_action'>
|
||||
{selectionModel && selectionModel.length >= 1 && actionmass_ftion_val && actionmass_ftion_val != "" && <nav className='block_en_mass_bton_action'>
|
||||
<Popup
|
||||
trigger={<Button className="bton_enreg" style={{ "width": "90%" }}>
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -122,6 +122,7 @@ import ScoreboardIcon from '@mui/icons-material/Scoreboard';
|
|||
import Groupe_Apprenant from "./Groupe_Apprenant";
|
||||
import Session_Admission_FI from "./Session_Admission_FI";
|
||||
import Module_Plateform_Admin_Partners from "./Module_Plateform_Admin_Partners";
|
||||
import Module_Inscrit_Suivi_Paiement from "./Module_Inscrit_Suivi_Paiement";
|
||||
|
||||
|
||||
import Equipe_Team from "./Equipe_Team";
|
||||
|
|
@ -295,6 +296,7 @@ const menuData_Full = [
|
|||
children: [
|
||||
{ label: "Devis", name: "Devis", icon: WalletCards, items_color: "#dbff75", },
|
||||
{ label: "Factures & Avoirs", name: "Factures & Avoirs", icon: HandCoins, items_color: "#dbff75" },
|
||||
{ label: "Suivi Paiement", name: "Suivi Paiement", icon: HandCoins, items_color: "#dbff75" },
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -449,6 +451,7 @@ const menuData_Ftion_Initiale = [
|
|||
children: [
|
||||
{ label: "Devis", name: "Devis", icon: WalletCards, items_color: "#dbff75", },
|
||||
{ label: "Factures & Avoirs", name: "Factures & Avoirs", icon: HandCoins, items_color: "#dbff75" },
|
||||
{ label: "Suivi Paiement", name: "Suivi Paiement", icon: HandCoins, items_color: "#dbff75" },
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -591,6 +594,7 @@ const menuData_Ftion_Continue = [
|
|||
children: [
|
||||
{ label: "Devis", name: "Devis", icon: WalletCards, items_color: "#dbff75", },
|
||||
{ label: "Factures & Avoirs", name: "Factures & Avoirs", icon: HandCoins, items_color: "#dbff75" },
|
||||
{ label: "Suivi Paiement", name: "Suivi Paiement", icon: HandCoins, items_color: "#dbff75" },
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -759,6 +763,8 @@ const Partner = (props) => {
|
|||
mes_cmd_func();
|
||||
} else if (local_menu.toLocaleLowerCase() === "Factures & Avoirs".toLocaleLowerCase()) {
|
||||
mes_factures_client_func();
|
||||
} else if (local_menu.toLocaleLowerCase() === "Suivi Paiement".toLocaleLowerCase()) {
|
||||
mes_suivi_paiement_func();
|
||||
} else if (local_menu.toLocaleLowerCase() === "Prix Achat".toLocaleLowerCase()) {
|
||||
prix_achat_func();
|
||||
} else if (local_menu.toLocaleLowerCase() === "Contenu LMS".toLocaleLowerCase()) {
|
||||
|
|
@ -1294,7 +1300,7 @@ const Partner = (props) => {
|
|||
'my_partners', 'my_invoices', 'my_prix_achat', 'my_e_learning', 'my_configuration', 'my_tbd', 'my_ue', 'domaine_categorie_metier',
|
||||
'config_formation_initiale', 'note_evaluation', 'groupe_apprenant', 'part_produit_service', 'monactivite', 'mon_tableau_de_bord',
|
||||
'tous_mes_document', 'equipe_team', 'conseil_class', 'autre_rh', 'jury', 'conseil_classe', 'internal_email', "admission_process",
|
||||
"admission_session", 'config_plateforme_partners', 'administration_pedagogique', 'competence_pedagogique', 'mes_module_ftion']
|
||||
"admission_session", 'config_plateforme_partners', 'administration_pedagogique', 'competence_pedagogique', 'mes_module_ftion', 'suivi_paiement']
|
||||
for (let i = 0; i < list_menu_header_names.length; i++) {
|
||||
if (document.getElementsByName(String(list_menu_header_names[i])) && document.getElementsByName(String(list_menu_header_names[i]))[0]) {
|
||||
document.getElementsByName(String(list_menu_header_names[i]))[0].style.fontWeight = "400";
|
||||
|
|
@ -1312,7 +1318,7 @@ const Partner = (props) => {
|
|||
'my_partners', 'my_invoices', 'my_prix_achat', 'my_e_learning', 'my_configuration', 'my_tbd', 'my_ue', 'domaine_categorie_metier',
|
||||
'config_formation_initiale', 'note_evaluation', 'groupe_apprenant', 'part_produit_service', 'monactivite', 'mon_tableau_de_bord',
|
||||
'tous_mes_document', 'equipe_team', 'conseil_class', 'autre_rh', 'jury', 'conseil_classe', 'internal_email', "admission_process",
|
||||
"admission_session", 'config_plateforme_partners', 'administration_pedagogique', 'competence_pedagogique']
|
||||
"admission_session", 'config_plateforme_partners', 'administration_pedagogique', 'competence_pedagogique', 'suivi_paiement']
|
||||
for (let i = 0; i < list_menu_header_names.length; i++) {
|
||||
if (document.getElementsByName(String(list_menu_header_names[i])) && document.getElementsByName(String(list_menu_header_names[i]))[0]) {
|
||||
document.getElementsByName(String(list_menu_header_names[i]))[0].style.fontWeight = "400";
|
||||
|
|
@ -1642,6 +1648,24 @@ 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 ")
|
||||
setmenu("suivi_paiement");
|
||||
setapiexcelimportmessage();
|
||||
setformation_file_name();
|
||||
Update_Connexion_Context("mysy_last_menu_visited", "/Partner/suivi_paiement");
|
||||
history.push("/Partner/suivi_paiement");
|
||||
document.body.style.backgroundColor = "#ffffff";
|
||||
inactive_active_menu_header("my_invoices");
|
||||
} else {
|
||||
Dialog_1_handle_acces_insuffisant("Vos droits ne sont pas suffisants pour accéder à la gestion des documents");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DisplayStat(event) {
|
||||
var security_retval = check_user_acces_right("formation", "read");
|
||||
if (security_retval === 1) {
|
||||
|
|
@ -2198,8 +2222,10 @@ const Partner = (props) => {
|
|||
setapiexcelimportmessage();
|
||||
setformation_file_name();
|
||||
|
||||
|
||||
history.push("/Partner/affichage");
|
||||
if (local_is_ftion_initiale === "1")
|
||||
history.push("/Partner/affichage_fi");
|
||||
else
|
||||
history.push("/Partner/affichage");
|
||||
|
||||
inactive_active_menu_header("my_class");
|
||||
var server_address = "127.0.0.1";
|
||||
|
|
@ -4167,6 +4193,15 @@ const Partner = (props) => {
|
|||
</div>
|
||||
}
|
||||
|
||||
{String(menu) === "suivi_paiement" &&
|
||||
<div className="div_droite">
|
||||
<Module_Inscrit_Suivi_Paiement conntected_employee_id={conntected_employee_id} read_access={check_user_acces_right("facture_client", "read")}
|
||||
write_access={check_user_acces_right("facture_client", "write")}
|
||||
check_user_acces_right={check_user_acces_right} />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
{String(menu) === "prix_achat" &&
|
||||
<div className="div_droite">
|
||||
<Partner_Prix_Achat conntected_employee_id={conntected_employee_id} read_access={check_user_acces_right("prix_achat", "read")} write_access={check_user_acces_right("prix_achat", "write")} />
|
||||
|
|
@ -4691,6 +4726,16 @@ const Partner = (props) => {
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<hr className="my_hr" />
|
||||
<Tooltip className="tooltip_css" id="tooltip_id_1808" />
|
||||
<a data-tooltip-id="tooltip_id_1808" data-tooltip-html="SUIVI PAIEMENT">
|
||||
{String(menu) !== "suivi_paiement" && <div className="sousmenu" onClick={mes_suivi_paiement_func}><FcKindle className="icone_menu" style={{ color: '#dbff75' }} /> </div>}
|
||||
{String(menu) === "suivi_paiement" && <div className="sousmenu_selected" onClick={mes_suivi_paiement_func}> <FcCollect FcKindle="icone_menu" style={{ color: '#dbff75' }} /> </div>}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<hr className="my_hr" />
|
||||
<Tooltip className="tooltip_css" id="tooltip_id_19" />
|
||||
|
|
@ -5168,6 +5213,14 @@ const Partner = (props) => {
|
|||
</div>
|
||||
}
|
||||
|
||||
{String(menu) === "suivi_paiement" &&
|
||||
<div className="div_droite div_droite_ralonge">
|
||||
<Module_Inscrit_Suivi_Paiement conntected_employee_id={conntected_employee_id} read_access={check_user_acces_right("facture_client", "read")}
|
||||
write_access={check_user_acces_right("facture_client", "write")}
|
||||
check_user_acces_right={check_user_acces_right} />
|
||||
</div>
|
||||
}
|
||||
|
||||
{String(menu) === "prix_achat" &&
|
||||
<div className="div_droite div_droite_ralonge">
|
||||
<Partner_Prix_Achat conntected_employee_id={conntected_employee_id} read_access={check_user_acces_right("prix_achat", "read")} write_access={check_user_acces_right("prix_achat", "write")} />
|
||||
|
|
@ -5356,7 +5409,7 @@ const Partner = (props) => {
|
|||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
{String(menu) === "monactivite" && <div className="div_droite div_droite_ralonge">
|
||||
<PartnerClientActivite conntected_employee_id={conntected_employee_id} check_user_acces_right={check_user_acces_right}
|
||||
email={conntected_employee_email} related_collection={"partnair_account"} update_nb_activite_en_retard={update_nb_activite_en_retard}
|
||||
|
|
|
|||
|
|
@ -3465,7 +3465,7 @@ const Partner_Facture = (props) => {
|
|||
|
||||
const New_Option_Filter = [
|
||||
{ "id": "ref_interne_invoice", "label": "Num Facture", "value": "ref_interne_invoice" },
|
||||
{ "id": "client_nom", "label": "Nom Client", "value": "client_nom" },
|
||||
{ "id": "client_nom", "label": "Nom / Raison Sociale / Tél / mail", "value": "client_nom" },
|
||||
{ "id": "solde", "label": "Reste à payer", "value": "solde" },
|
||||
{ "id": "montant_ttc", "label": "Montant TTC", "value": "montant_ttc" },
|
||||
{ "id": "date_echeance", "label": "Echéance", "value": "date_echeance" },
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ import {
|
|||
|
||||
import { AudioRecorder, useAudioRecorder } from 'react-audio-voice-recorder';
|
||||
|
||||
import VideoRecorder from "./video_recorder/video_recorder";
|
||||
|
||||
//import ContextMenu from 'react-jsx-context-menu';
|
||||
import { ContextMenu, ContextMenuTrigger } from "react-contextmenu";
|
||||
import * as XLSX from 'xlsx';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,236 @@
|
|||
import React, { useRef, useState } from "react";
|
||||
import "./video_recorder_css.css";
|
||||
|
||||
export default function VideoRecorder() {
|
||||
const liveVideoRef = useRef(null);
|
||||
const recordedVideoRef = useRef(null);
|
||||
const mediaRecorderRef = useRef(null);
|
||||
const streamRef = useRef(null);
|
||||
const chunksRef = useRef([]);
|
||||
|
||||
const [isCameraReady, setIsCameraReady] = useState(false);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [videoUrl, setVideoUrl] = useState(null);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
const startCamera = async () => {
|
||||
try {
|
||||
setErrorMessage("");
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
width: { ideal: 1280 },
|
||||
height: { ideal: 720 },
|
||||
},
|
||||
audio: true,
|
||||
});
|
||||
|
||||
streamRef.current = stream;
|
||||
|
||||
if (liveVideoRef.current) {
|
||||
liveVideoRef.current.srcObject = stream;
|
||||
}
|
||||
|
||||
setIsCameraReady(true);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setErrorMessage(
|
||||
"Impossible d'accéder à la caméra ou au micro. Vérifiez les autorisations du navigateur."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const stopCamera = () => {
|
||||
if (streamRef.current) {
|
||||
streamRef.current.getTracks().forEach((track) => track.stop());
|
||||
streamRef.current = null;
|
||||
}
|
||||
|
||||
if (liveVideoRef.current) {
|
||||
liveVideoRef.current.srcObject = null;
|
||||
}
|
||||
|
||||
setIsCameraReady(false);
|
||||
setIsRecording(false);
|
||||
};
|
||||
|
||||
const startRecording = () => {
|
||||
if (!streamRef.current) {
|
||||
setErrorMessage("Veuillez d'abord activer la caméra.");
|
||||
return;
|
||||
}
|
||||
|
||||
chunksRef.current = [];
|
||||
setVideoUrl(null);
|
||||
|
||||
const options = {
|
||||
mimeType: "video/webm; codecs=vp9",
|
||||
};
|
||||
|
||||
let mediaRecorder;
|
||||
|
||||
try {
|
||||
mediaRecorder = new MediaRecorder(streamRef.current, options);
|
||||
} catch (error) {
|
||||
mediaRecorder = new MediaRecorder(streamRef.current);
|
||||
}
|
||||
|
||||
mediaRecorderRef.current = mediaRecorder;
|
||||
|
||||
mediaRecorder.ondataavailable = (event) => {
|
||||
if (event.data && event.data.size > 0) {
|
||||
chunksRef.current.push(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = () => {
|
||||
const blob = new Blob(chunksRef.current, {
|
||||
type: "video/webm",
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
setVideoUrl(url);
|
||||
|
||||
if (recordedVideoRef.current) {
|
||||
recordedVideoRef.current.src = url;
|
||||
}
|
||||
};
|
||||
|
||||
mediaRecorder.start();
|
||||
setIsRecording(true);
|
||||
};
|
||||
|
||||
const stopRecording = () => {
|
||||
if (
|
||||
mediaRecorderRef.current &&
|
||||
mediaRecorderRef.current.state !== "inactive"
|
||||
) {
|
||||
mediaRecorderRef.current.stop();
|
||||
}
|
||||
|
||||
setIsRecording(false);
|
||||
};
|
||||
|
||||
const downloadVideo = () => {
|
||||
if (!videoUrl) return;
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = videoUrl;
|
||||
link.download = `enregistrement-video-${Date.now()}.webm`;
|
||||
link.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="video-recorder">
|
||||
<div className="video-recorder__header">
|
||||
<div>
|
||||
<span className="video-recorder__badge">Module vidéo</span>
|
||||
<h2>Enregistrement vidéo</h2>
|
||||
<p>
|
||||
Activez la caméra, lancez l’enregistrement, puis récupérez la vidéo
|
||||
générée.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<div className="video-recorder__alert">
|
||||
{errorMessage}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="video-recorder__grid">
|
||||
<div className="video-recorder__card">
|
||||
<div className="video-recorder__card-header">
|
||||
<h3>Aperçu caméra</h3>
|
||||
<span className={isCameraReady ? "status active" : "status"}>
|
||||
{isCameraReady ? "Caméra active" : "Caméra inactive"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="video-recorder__video-wrapper">
|
||||
<video
|
||||
ref={liveVideoRef}
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
className="video-recorder__video"
|
||||
/>
|
||||
|
||||
{!isCameraReady && (
|
||||
<div className="video-recorder__placeholder">
|
||||
Caméra non activée
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isRecording && (
|
||||
<div className="video-recorder__recording-indicator">
|
||||
<span></span>
|
||||
Enregistrement en cours
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="video-recorder__actions">
|
||||
{!isCameraReady ? (
|
||||
<button className="btn primary" onClick={startCamera}>
|
||||
Activer la caméra
|
||||
</button>
|
||||
) : (
|
||||
<button className="btn ghost" onClick={stopCamera}>
|
||||
Couper la caméra
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isRecording ? (
|
||||
<button
|
||||
className="btn danger"
|
||||
onClick={startRecording}
|
||||
disabled={!isCameraReady}
|
||||
>
|
||||
Démarrer l’enregistrement
|
||||
</button>
|
||||
) : (
|
||||
<button className="btn dark" onClick={stopRecording}>
|
||||
Arrêter
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="video-recorder__card">
|
||||
<div className="video-recorder__card-header">
|
||||
<h3>Vidéo enregistrée</h3>
|
||||
<span className={videoUrl ? "status active" : "status"}>
|
||||
{videoUrl ? "Disponible" : "En attente"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="video-recorder__video-wrapper">
|
||||
{videoUrl ? (
|
||||
<video
|
||||
ref={recordedVideoRef}
|
||||
controls
|
||||
className="video-recorder__video"
|
||||
/>
|
||||
) : (
|
||||
<div className="video-recorder__placeholder">
|
||||
Votre vidéo apparaîtra ici après l’arrêt de l’enregistrement.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="video-recorder__actions">
|
||||
<button
|
||||
className="btn primary"
|
||||
onClick={downloadVideo}
|
||||
disabled={!videoUrl}
|
||||
>
|
||||
Télécharger la vidéo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
.video-recorder {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding: 32px;
|
||||
background: #f4f7fb;
|
||||
font-family: "DM Sans", Arial, Helvetica, sans-serif;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.video-recorder__header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.video-recorder__badge {
|
||||
display: inline-flex;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(16, 66, 119, 0.1);
|
||||
color: #104277;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.video-recorder__header h2 {
|
||||
margin: 0;
|
||||
font-size: 30px;
|
||||
color: #104277;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.video-recorder__header p {
|
||||
margin: 8px 0 0;
|
||||
color: #637083;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.video-recorder__alert {
|
||||
background: #fff1f1;
|
||||
color: #b42318;
|
||||
border: 1px solid #ffd0d0;
|
||||
padding: 14px 16px;
|
||||
border-radius: 14px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.video-recorder__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.video-recorder__card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e7edf5;
|
||||
border-radius: 24px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 18px 45px rgba(16, 66, 119, 0.08);
|
||||
}
|
||||
|
||||
.video-recorder__card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.video-recorder__card-header h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #f1f3f7;
|
||||
color: #737d8c;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status.active {
|
||||
background: rgba(24, 160, 88, 0.1);
|
||||
color: #16834a;
|
||||
}
|
||||
|
||||
.video-recorder__video-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 10;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #102a44, #104277);
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.video-recorder__video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
.video-recorder__placeholder {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.video-recorder__recording-indicator {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(185, 28, 28, 0.92);
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
box-shadow: 0 10px 30px rgba(185, 28, 28, 0.3);
|
||||
}
|
||||
|
||||
.video-recorder__recording-indicator span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: #ffffff;
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
.video-recorder__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none;
|
||||
min-height: 42px;
|
||||
padding: 0 16px;
|
||||
border-radius: 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
.btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: #104277;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.danger {
|
||||
background: #dc2626;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.dark {
|
||||
background: #172033;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn.ghost {
|
||||
background: #eef3f9;
|
||||
color: #104277;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.45;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.video-recorder {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.video-recorder__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
import TestUrl_New_26 from "../components/Test_Files/MesInscriptions";
|
||||
import Navigation from "../components/Navigation";
|
||||
import VideoRecorder from "./../components/video_recorder/video_recorder";
|
||||
export default function Recherche() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Navigation />
|
||||
<TestUrl_New_26 />
|
||||
<VideoRecorder />
|
||||
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -222,6 +222,7 @@
|
|||
@import "./components/module_bloc_competence_pedagogique";
|
||||
@import "./components/module_domaine_competence_pedagogique";
|
||||
@import "./components/module_gestion_stage";
|
||||
@import "./components/module_inscrit_suivi_paiement";
|
||||
|
||||
|
||||
// New Design 2025
|
||||
|
|
|
|||
Loading…
Reference in New Issue