mirror of
https://github.com/Floriansylvain/Exo-technique-VIE-Barcelone-SaaS-HRTech.git
synced 2026-08-19 19:53:23 +02:00
feat: initial impl w/ styles, scripts, & server-side logic (steps 1, 2, 3)
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dynamic-div ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dynamic-div li {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.garage-bullet {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.garage-color {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.car-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
.dynamic-div .car-item:hover {
|
||||
background-color: rgba(59, 130, 246, 0.06);
|
||||
}
|
||||
.car-item:focus {
|
||||
outline: 2px solid rgba(59,130,246,0.18);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: crimson;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
.toolbar {
|
||||
background: #1f2937;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 14px;
|
||||
font-family: Inter, system-ui, Arial, sans-serif
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
background: #fff;
|
||||
color: #111;
|
||||
border: 0;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.toolbar button.active {
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15)
|
||||
}
|
||||
|
||||
.toolbar button.toolbar-close {
|
||||
margin-left: auto;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08)
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: crimson;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export async function fetchHtml(moduleName, scriptName, clientName = '', extraParams = {}) {
|
||||
if (!moduleName || !scriptName) throw new Error('module/script required');
|
||||
|
||||
const params = new URLSearchParams({ module: moduleName, script: scriptName });
|
||||
|
||||
if (clientName) params.set('client', clientName);
|
||||
|
||||
Object.keys(extraParams || {}).forEach(k => {
|
||||
if (extraParams[k] !== undefined && extraParams[k] !== null) params.set(k, String(extraParams[k]));
|
||||
});
|
||||
|
||||
const url = 'src/ajax_handler.php?' + params.toString();
|
||||
const response = await fetch(url, { method: 'GET', credentials: 'same-origin' });
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
const message = text || `Request failed: ${response.status}`;
|
||||
const err = new Error(message);
|
||||
err.httpStatus = response.status;
|
||||
throw err;
|
||||
}
|
||||
|
||||
return response.text();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export function renderError(container, message) {
|
||||
if (!container) return;
|
||||
try {
|
||||
const $c = (container.jquery) ? container : (typeof container === 'string' ? $(container) : $(container));
|
||||
$c.html(`<div class="error-message">${String(message)}</div>`);
|
||||
} catch (e) {
|
||||
if (container instanceof Element) container.innerHTML = `<div class="error-message">${String(message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
export function insertHtml(container, html) {
|
||||
if (!container) return;
|
||||
try {
|
||||
const $c = (container.jquery) ? container : (typeof container === 'string' ? $(container) : $(container));
|
||||
$c.html(html);
|
||||
} catch (e) {
|
||||
if (container instanceof Element) container.innerHTML = html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { fetchHtml } from './apiClient.js';
|
||||
import { renderError, insertHtml } from './domModule.js';
|
||||
|
||||
function getCookie(name) {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
return match ? decodeURIComponent(match[2]) : null;
|
||||
}
|
||||
|
||||
async function loadDynamic() {
|
||||
const $container = $('.dynamic-div').first();
|
||||
if (!$container.length) return;
|
||||
const moduleName = $container.data('module') || '';
|
||||
const scriptName = $container.data('script') || '';
|
||||
const clientName = getCookie('client') || '';
|
||||
if (!moduleName || !scriptName) return renderError($container, 'Missing data-module or data-script.');
|
||||
try {
|
||||
const html = await fetchHtml(moduleName, scriptName, clientName);
|
||||
insertHtml($container, html);
|
||||
attachCarClickHandlers($container, clientName);
|
||||
} catch (err) {
|
||||
renderError($container, err.message || 'Network error while loading dynamic content.');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(loadDynamic);
|
||||
|
||||
window.loadDynamic = loadDynamic;
|
||||
|
||||
function attachCarClickHandlers($container, clientName) {
|
||||
if (!$container || !$container.length) return;
|
||||
if ($container.data('_carHandlerAttached')) return;
|
||||
$container.on('click', '.car-item', async function (ev) {
|
||||
const $el = $(this);
|
||||
const id = $el.data('car-id') || $el.attr('data-car-id');
|
||||
if (!id) return;
|
||||
try {
|
||||
const html = await fetchHtml('cars', 'edit', clientName, { id });
|
||||
insertHtml($container, html);
|
||||
} catch (err) {
|
||||
renderError($container, err.message || 'Network error while loading details.');
|
||||
}
|
||||
});
|
||||
$container.data('_carHandlerAttached', '1');
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
class Toolbar {
|
||||
constructor(containerClass = 'toolbar') {
|
||||
this.$container = $('<div>').addClass(containerClass);
|
||||
this.clientMapping = [
|
||||
{ label: 'Client A', value: 'clienta' },
|
||||
{ label: 'Client B', value: 'clientb' },
|
||||
{ label: 'Client C', value: 'clientc' }
|
||||
];
|
||||
this.injectStylesheet('assets/css/toolbar.css');
|
||||
this.render();
|
||||
this.activateExistingClient();
|
||||
}
|
||||
|
||||
injectStylesheet(href) {
|
||||
const $link = $('<link>', { rel: 'stylesheet', href });
|
||||
$('head').append($link);
|
||||
}
|
||||
|
||||
setCookieValue(name, value, maxAgeSeconds = 86400) {
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAgeSeconds}; SameSite=Lax`;
|
||||
}
|
||||
|
||||
markActiveButton($button) {
|
||||
this.$container.find('button').removeClass('active');
|
||||
if ($button && $button.length) $button.addClass('active');
|
||||
}
|
||||
|
||||
render() {
|
||||
this.clientMapping.forEach(mapping => {
|
||||
const $button = $('<button>').text(`Simuler ${mapping.label}`).attr('data-client', mapping.value);
|
||||
$button.on('click', () => {
|
||||
this.setCookieValue('client', mapping.value);
|
||||
this.markActiveButton($button);
|
||||
if (window.loadDynamic && typeof window.loadDynamic === 'function') window.loadDynamic();
|
||||
});
|
||||
this.$container.append($button);
|
||||
});
|
||||
|
||||
const $closeButton = $('<button>').text('Fermer').addClass('toolbar-close');
|
||||
$closeButton.on('click', () => {
|
||||
this.$container.remove();
|
||||
document.body.style.paddingTop = null;
|
||||
});
|
||||
this.$container.append($closeButton);
|
||||
|
||||
$('body').prepend(this.$container);
|
||||
}
|
||||
|
||||
getCookieValue(name) {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
return match ? decodeURIComponent(match[2]) : null;
|
||||
}
|
||||
|
||||
activateExistingClient() {
|
||||
const existingClient = this.getCookieValue('client');
|
||||
if (!existingClient) return;
|
||||
const $btn = this.$container.find(`button[data-client="${existingClient}"]`);
|
||||
if ($btn.length) this.markActiveButton($btn);
|
||||
}
|
||||
}
|
||||
|
||||
new Toolbar();
|
||||
|
||||
export default Toolbar;
|
||||
Reference in New Issue
Block a user