08/05/2026 - 17h

branche_01062025
Cherif 2026-05-08 19:11:29 +02:00
parent 7cfeaefafe
commit e5ab6f881e
11 changed files with 5075 additions and 122 deletions

View File

@ -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">
&nbsp; <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">
&nbsp; <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' }}>

View File

@ -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 &nbsp;&nbsp;</MenuItem>
<MenuItem value="publier" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Publier &nbsp;</MenuItem>
<MenuItem value="depublier" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Dépublier &nbsp;</MenuItem>
<MenuItem value="dupliquer" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Dupliquer &nbsp;</MenuItem>
<MenuItem value="supprimer" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Supprimer &nbsp;</MenuItem>
<MenuItem value="exporter" style={{ "paddingLeft": "5px", "textAlign": "left", "width": "100%" }}>Exporter &nbsp;</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

View File

@ -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' }} />&nbsp;</div>}
{String(menu) === "suivi_paiement" && <div className="sousmenu_selected" onClick={mes_suivi_paiement_func}> <FcCollect FcKindle="icone_menu" style={{ color: '#dbff75' }} />&nbsp;</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}

View File

@ -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" },

View File

@ -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';

View File

@ -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 lenregistrement, 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 lenregistrement
</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 larrêt de lenregistrement.
</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>
);
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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