code

<!doctype html>

<html lang= »fr »>

<head>

<meta charset= »UTF-8″>

<meta name= »viewport » content= »width=device-width, initial-scale=1.0″>

<title>Inscription Réunion Parents</title>

<script src= »/_sdk/data_sdk.js »></script>

<script src= »/_sdk/element_sdk.js »></script>

<script src= »https://cdn.tailwindcss.com »></script>

<style>

body {

box-sizing: border-box;

height: 100%;

}

html {

height: 100%;

}

.form-container {

background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

}

.loading-spinner {

border: 3px solid #f3f4f6;

border-top: 3px solid #3b82f6;

border-radius: 50%;

width: 20px;

height: 20px;

animation: spin 1s linear infinite;

}

@keyframes spin {

0% { transform: rotate(0deg); }

100% { transform: rotate(360deg); }

}

.toast {

position: fixed;

top: 20px;

right: 20px;

z-index: 1000;

transform: translateX(100%);

transition: transform 0.3s ease-in-out;

}

.toast.show {

transform: translateX(0);

}

</style>

<style>@view-transition { navigation: auto; }</style>

</head>

<body>

<main class= »form-container min-h-full py-8 px-4″>

<div class= »max-w-2xl mx-auto »>

<div class= »bg-white rounded-lg shadow-xl p-8″>

<header class= »text-center mb-8″>

<h1 id= »titre-formulaire » class= »text-3xl font-bold text-gray-800 mb-4″>Inscription à la réunion des parents</h1>

<div class= »bg-blue-50 border-l-4 border-blue-400 p-4 mb-6″>

<div class= »flex »>

<div class= »ml-3″>

<p class= »text-sm text-blue-700″><strong>Date :</strong> <span id= »date-reunion »>Mardi 10 décembre 2024</span><br><strong>Lieu :</strong> <span id= »lieu-reunion »>Salle polyvalente de l’école</span></p>

</div>

</div>

</div>

</header>

<div id= »no-slots-message » class= »hidden bg-red-50 border border-red-200 rounded-md p-6 text-center »>

<div class= »flex flex-col items-center »>

<svg class= »h-12 w-12 text-red-400 mb-4″ fill= »none » viewbox= »0 0 24 24″ stroke= »currentColor »><path stroke-linecap= »round » stroke-linejoin= »round » stroke-width= »2″ d= »M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z » />

</svg>

<h2 class= »text-xl font-bold text-red-800 mb-2″>Inscriptions complètes</h2>

<p class= »text-red-700″>Tous les créneaux de rendez-vous sont désormais complets.</p>

<p class= »text-red-700 mt-2″>Il n’est plus possible de s’inscrire pour cette réunion.</p>

</div>

</div>

<form id= »inscription-form » class= »space-y-6″>

<div class= »grid grid-cols-1 md:grid-cols-2 gap-6″>

<div><label for= »nom-parent » class= »block text-sm font-medium text-gray-700 mb-2″>Nom du parent *</label> <input type= »text » id= »nom-parent » name= »nom-parent » required class= »w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent »>

</div>

<div><label for= »prenom-parent » class= »block text-sm font-medium text-gray-700 mb-2″>Prénom du parent *</label> <input type= »text » id= »prenom-parent » name= »prenom-parent » required class= »w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent »>

</div>

</div>

<div class= »grid grid-cols-1 md:grid-cols-2 gap-6″>

<div><label for= »nom-enfant » class= »block text-sm font-medium text-gray-700 mb-2″>Nom de l’enfant *</label> <input type= »text » id= »nom-enfant » name= »nom-enfant » required class= »w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent »>

</div>

<div><label for= »classe-enfant » class= »block text-sm font-medium text-gray-700 mb-2″>Classe *</label> <input type= »text » id= »classe-enfant » name= »classe-enfant » value= »P4A » readonly class= »w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-100 text-gray-600 cursor-not-allowed »>

</div>

</div>

<div><label for= »creneau-prefere » class= »block text-sm font-medium text-gray-700 mb-2″>Créneau préféré *</label> <select id= »creneau-prefere » name= »creneau-prefere » required class= »w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent »> <option value= » »>Sélectionnez un créneau disponible</option> </select>

<p class= »text-sm text-gray-600 mt-1″>Les créneaux déjà réservés ne sont pas affichés</p>

</div>

<div class= »bg-gray-50 p-4 rounded-md »>

<div class= »flex items-start »><input type= »checkbox » id= »conditions » name= »conditions » required class= »mt-1 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded »> <label for= »conditions » class= »ml-3 text-sm text-gray-700″> J’accepte que mes données personnelles soient utilisées dans le cadre de l’organisation de cette réunion et je confirme ma participation. * </label>

</div>

</div>

<div class= »text-center »><button type= »submit » id= »submit-btn » class= »bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-lg transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center mx-auto »> <span id= »submit-text »>Confirmer mon inscription</span>

<div id= »submit-spinner » class= »loading-spinner ml-2 hidden »></div></button>

</div>

</form>

<div id= »success-message » class= »hidden mt-8 bg-green-50 border border-green-200 rounded-md p-4″>

<div class= »flex »>

<div class= »flex-shrink-0″>

<svg class= »h-5 w-5 text-green-400″ viewbox= »0 0 20 20″ fill= »currentColor »><path fill-rule= »evenodd » d= »M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z » clip-rule= »evenodd » />

</svg>

</div>

<div class= »ml-3″>

<p id= »message-confirmation » class= »text-sm font-medium text-green-800″>Votre inscription a été enregistrée avec succès !</p>

<p id= »rdv-details » class= »text-sm text-green-700 mt-1 font-medium »>Rendez-vous confirmé le jeudi 18 décembre 2025</p>

</div>

</div>

</div>

</div>

</div>

</main>

<div id= »toast » class= »toast bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg »><span id= »toast-message »>Une erreur est survenue</span>

</div>

<script>

let currentRecordCount = 0;

let reservedSlots = new Set();

 

// Génération des 15 créneaux de 10 minutes avec pauses

const generateTimeSlots = () => {

const slots = [];

let startHour = 15;

let startMinute = 0;

 

for (let i = 0; i < 15; i++) {

// Ajouter une pause de 10 minutes après chaque 5 rendez-vous

if (i > 0 && i % 5 === 0) {

startMinute += 10;

if (startMinute >= 60) {

startHour += 1;

startMinute -= 60;

}

}

 

const endMinute = startMinute + 10;

const endHour = endMinute >= 60 ? startHour + 1 : startHour;

const finalEndMinute = endMinute >= 60 ? endMinute – 60 : endMinute;

 

const startTime = `${startHour}h${startMinute.toString().padStart(2, ‘0’)}`;

const endTime = `${endHour}h${finalEndMinute.toString().padStart(2, ‘0’)}`;

 

slots.push(`${startTime} – ${endTime}`);

 

startMinute += 10;

if (startMinute >= 60) {

startHour += 1;

startMinute -= 60;

}

}

 

return slots;

};

 

const allTimeSlots = generateTimeSlots();

 

const defaultConfig = {

titre_formulaire: « Inscription à la réunion des parents »,

date_reunion: « Jeudi 18 décembre 2025 »,

lieu_reunion: « En classe »,

message_confirmation: « Votre inscription a été enregistrée avec succès ! »,

background_color: « #667eea »,

surface_color: « #ffffff »,

text_color: « #374151 »,

primary_action_color: « #2563eb »,

secondary_action_color: « #6b7280 »,

font_family: « system-ui »,

font_size: 16

};

 

const dataHandler = {

onDataChanged(data) {

currentRecordCount = data.length;

 

// Mettre à jour la liste des créneaux réservés

reservedSlots.clear();

data.forEach(inscription => {

if (inscription.creneau_prefere) {

reservedSlots.add(inscription.creneau_prefere);

}

});

 

// Mettre à jour les options disponibles

updateAvailableSlots();

}

};

 

function updateAvailableSlots() {

const select = document.getElementById(‘creneau-prefere’);

const currentValue = select.value;

const form = document.getElementById(‘inscription-form’);

const noSlotsMessage = document.getElementById(‘no-slots-message’);

 

// Vider les options existantes sauf la première

select.innerHTML = ‘<option value= » »>Sélectionnez un créneau disponible</option>’;

 

// Ajouter seulement les créneaux disponibles

allTimeSlots.forEach(slot => {

if (!reservedSlots.has(slot)) {

const option = document.createElement(‘option’);

option.value = slot;

option.textContent = slot;

select.appendChild(option);

}

});

 

// Restaurer la valeur si elle est encore disponible

if (currentValue && !reservedSlots.has(currentValue)) {

select.value = currentValue;

}

 

// Afficher un message si tous les créneaux sont pris

const availableCount = allTimeSlots.length – reservedSlots.size;

const messageElement = select.nextElementSibling;

if (availableCount === 0) {

// Masquer le formulaire et afficher le message d’inscriptions complètes

form.style.display = ‘none’;

noSlotsMessage.classList.remove(‘hidden’);

} else {

// Afficher le formulaire et masquer le message d’inscriptions complètes

form.style.display = ‘block’;

noSlotsMessage.classList.add(‘hidden’);

 

messageElement.textContent = `${availableCount} créneaux disponibles sur ${allTimeSlots.length}`;

messageElement.className = « text-sm text-gray-600 mt-1 »;

select.disabled = false;

}

}

 

function showToast(message, isError = true) {

const toast = document.getElementById(‘toast’);

const toastMessage = document.getElementById(‘toast-message’);

 

toastMessage.textContent = message;

toast.className = `toast px-6 py-3 rounded-lg shadow-lg ${isError ? ‘bg-red-500’ : ‘bg-green-500’} text-white`;

toast.classList.add(‘show’);

 

setTimeout(() => {

toast.classList.remove(‘show’);

}, 4000);

}

 

async function handleFormSubmit(event) {

event.preventDefault();

 

if (currentRecordCount >= 999) {

showToast(« Limite maximale de 999 inscriptions atteinte. Veuillez contacter l’administration. »);

return;

}

 

const submitBtn = document.getElementById(‘submit-btn’);

const submitText = document.getElementById(‘submit-text’);

const submitSpinner = document.getElementById(‘submit-spinner’);

 

// Show loading state

submitBtn.disabled = true;

submitText.textContent = ‘Inscription en cours…’;

submitSpinner.classList.remove(‘hidden’);

 

const formData = new FormData(event.target);

const inscriptionData = {

nom_parent: formData.get(‘nom-parent’),

prenom_parent: formData.get(‘prenom-parent’),

nom_enfant: formData.get(‘nom-enfant’),

classe_enfant: ‘P4A’,

creneau_prefere: formData.get(‘creneau-prefere’),

conditions_acceptees: formData.get(‘conditions’) === ‘on’,

date_inscription: new Date().toISOString()

};

 

try {

const result = await window.dataSdk.create(inscriptionData);

 

if (result.isOk) {

// Update success message with selected time slot

const selectedSlot = formData.get(‘creneau-prefere’);

document.getElementById(‘rdv-details’).textContent = `Rendez-vous confirmé le jeudi 18 décembre 2025 de ${selectedSlot}`;

 

// Show success message

document.getElementById(‘inscription-form’).style.display = ‘none’;

document.getElementById(‘success-message’).classList.remove(‘hidden’);

} else {

showToast(‘Erreur lors de l\’inscription. Veuillez réessayer.’);

}

} catch (error) {

showToast(‘Erreur lors de l\’inscription. Veuillez réessayer.’);

} finally {

// Reset loading state

submitBtn.disabled = false;

submitText.textContent = ‘Confirmer mon inscription’;

submitSpinner.classList.add(‘hidden’);

}

}

 

async function onConfigChange(config) {

const customFont = config.font_family || defaultConfig.font_family;

const baseSize = config.font_size || defaultConfig.font_size;

const baseFontStack = ‘system-ui, -apple-system, sans-serif’;

 

// Update text content

document.getElementById(‘titre-formulaire’).textContent = config.titre_formulaire || defaultConfig.titre_formulaire;

document.getElementById(‘date-reunion’).textContent = config.date_reunion || defaultConfig.date_reunion;

document.getElementById(‘lieu-reunion’).textContent = config.lieu_reunion || defaultConfig.lieu_reunion;

document.getElementById(‘message-confirmation’).textContent = config.message_confirmation || defaultConfig.message_confirmation;

 

// Update colors

const backgroundColor = config.background_color || defaultConfig.background_color;

const surfaceColor = config.surface_color || defaultConfig.surface_color;

const textColor = config.text_color || defaultConfig.text_color;

const primaryActionColor = config.primary_action_color || defaultConfig.primary_action_color;

const secondaryActionColor = config.secondary_action_color || defaultConfig.secondary_action_color;

 

document.querySelector(‘.form-container’).style.background = `linear-gradient(135deg, ${backgroundColor} 0%, ${secondaryActionColor} 100%)`;

document.querySelector(‘.bg-white’).style.backgroundColor = surfaceColor;

 

// Update text colors

const textElements = document.querySelectorAll(‘h1, label, p, .text-gray-700, .text-gray-800’);

textElements.forEach(el => {

el.style.color = textColor;

});

 

// Update button colors

const submitButton = document.getElementById(‘submit-btn’);

submitButton.style.backgroundColor = primaryActionColor;

submitButton.style.borderColor = primaryActionColor;

 

// Update fonts

document.body.style.fontFamily = `${customFont}, ${baseFontStack}`;

 

// Update font sizes

document.getElementById(‘titre-formulaire’).style.fontSize = `${baseSize * 1.875}px`;

document.querySelectorAll(‘label’).forEach(el => {

el.style.fontSize = `${baseSize * 0.875}px`;

});

document.querySelectorAll(‘input, select, textarea’).forEach(el => {

el.style.fontSize = `${baseSize}px`;

});

document.getElementById(‘submit-btn’).style.fontSize = `${baseSize}px`;

}

 

// Initialize SDKs

async function initializeApp() {

try {

if (window.dataSdk) {

const initResult = await window.dataSdk.init(dataHandler);

if (!initResult.isOk) {

console.error(« Failed to initialize data SDK »);

}

}

 

if (window.elementSdk) {

window.elementSdk.init({

defaultConfig,

onConfigChange,

mapToCapabilities: (config) => ({

recolorables: [

{

get: () => config.background_color || defaultConfig.background_color,

set: (value) => {

config.background_color = value;

window.elementSdk.setConfig({ background_color: value });

}

},

{

get: () => config.surface_color || defaultConfig.surface_color,

set: (value) => {

config.surface_color = value;

window.elementSdk.setConfig({ surface_color: value });

}

},

{

get: () => config.text_color || defaultConfig.text_color,

set: (value) => {

config.text_color = value;

window.elementSdk.setConfig({ text_color: value });

}

},

{

get: () => config.primary_action_color || defaultConfig.primary_action_color,

set: (value) => {

config.primary_action_color = value;

window.elementSdk.setConfig({ primary_action_color: value });

}

},

{

get: () => config.secondary_action_color || defaultConfig.secondary_action_color,

set: (value) => {

config.secondary_action_color = value;

window.elementSdk.setConfig({ secondary_action_color: value });

}

}

],

borderables: [],

fontEditable: {

get: () => config.font_family || defaultConfig.font_family,

set: (value) => {

config.font_family = value;

window.elementSdk.setConfig({ font_family: value });

}

},

fontSizeable: {

get: () => config.font_size || defaultConfig.font_size,

set: (value) => {

config.font_size = value;

window.elementSdk.setConfig({ font_size: value });

}

}

}),

mapToEditPanelValues: (config) => new Map([

[« titre_formulaire », config.titre_formulaire || defaultConfig.titre_formulaire],

[« date_reunion », config.date_reunion || defaultConfig.date_reunion],

[« lieu_reunion », config.lieu_reunion || defaultConfig.lieu_reunion],

[« message_confirmation », config.message_confirmation || defaultConfig.message_confirmation]

])

});

}

 

// Set up form submission

document.getElementById(‘inscription-form’).addEventListener(‘submit’, handleFormSubmit);

 

// Initialiser les créneaux disponibles

updateAvailableSlots();

 

} catch (error) {

console.error(« Initialization error: », error);

}

}

 

// Initialize when page loads

initializeApp();

</script>

<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement(‘script’);d.innerHTML= »window.__CF$cv$params={r:’9a8a3ef0721670e9′,t:’MTc2NDg0MDIxNS4wMDAwMDA=’};var a=document.createElement(‘script’);a.nonce= »;a.src=’/cdn-cgi/challenge-platform/scripts/jsd/main.js’;document.getElementsByTagName(‘head’)[0].appendChild(a); »;b.getElementsByTagName(‘head’)[0].appendChild(d)}}if(document.body){var a=document.createElement(‘iframe’);a.height=1;a.width=1;a.style.position=’absolute’;a.style.top=0;a.style.left=0;a.style.border=’none’;a.style.visibility=’hidden’;document.body.appendChild(a);if(‘loading’!==document.readyState)c();else if(window.addEventListener)document.addEventListener(‘DOMContentLoaded’,c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);’loading’!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>

</html>

Vous aimerez aussi...