espace-paie-odentas/app/(app)/minima-ccn/ccnsvp/annexe1-data.tsx
2025-10-17 13:02:39 +02:00

402 lines
22 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useEffect, useState } from 'react';
const euro = (n: number) => new Intl.NumberFormat('fr-FR', {
minimumFractionDigits: Number.isInteger(n) ? 0 : 2,
maximumFractionDigits: 2
}).format(n) + '€';
// Données Annexe 1
const theatreData = [
{ cap: "≤ 200 places", j24: 118, j25_48: 100, j48p: 86, mens: 2000 },
{ cap: "≤ 300 places", j24: 125, j25_48: 106, j48p: 94, mens: 2184 },
{ cap: "301600 places", j24: 140, j25_48: 119, j48p: 105, mens: 2448 },
{ cap: "> 600 places", j24: 155, j25_48: 132, j48p: 116, mens: 2712 },
];
const musicalData = [
{ cat: "Comédien·ne 1er rôle / 1er·e chanteur·se soliste", r1_7: 177.89, r8_16: 164.34, cont: 129.59, mens24: 2956.98, mens151: 3110.13 },
{ cat: "Comédien·ne 2nd rôle", r1_7: 142.55, r8_16: 127.23, cont: 108.38, mens24: 2274.87, mens151: 2601.20 },
{ cat: "Comédien·ne", r1_7: 129.59, r8_16: 117.81, cont: 97.07, mens24: 2027.47, mens151: 2318.46 },
{ cat: "Artiste chorégraphique 1er rôle", r1_7: 177.89, r8_16: 160.22, cont: 129.59, mens24: 2886.29, mens151: 3110.13 },
{ cat: "Artiste chorégraphique 2nd rôle", r1_7: 166.11, r8_16: 146.08, cont: 108.38, mens24: 2575.28, mens151: 2601.20 },
{ cat: "Artiste chorégraphique d'ensemble", r1_7: 142.55, r8_16: 127.23, cont: 97.07, mens24: 2274.87, mens151: 2318.46 },
{ cat: "Artiste lyrique 1er emploi", r1_7: 177.89, r8_16: 164.34, cont: 129.59, mens24: 2886.29, mens151: 3110.13 },
{ cat: "Artiste lyrique 2nd emploi / Chanteur", r1_7: 142.55, r8_16: 127.23, cont: 108.38, mens24: 2274.87, mens151: 2601.20 },
{ cat: "Choristes de plateau", r1_7: 100.52, r8_16: 89.21, cont: 81.80, mens24: 1777.90, mens151: 1912.76 },
{ cat: "Doublure", r1_7: 100.52, r8_16: 89.21, cont: 79.70, mens24: 1732.24, mens151: 1912.76 },
{ cat: "Artiste de music-hall / numéro visuel", r1_7: 177.89, r8_16: 164.34, cont: 117.81, mens24: 2956.98, mens151: 2827.39 },
{ cat: "1er assistant·e des attractions", r1_7: 100.52, r8_16: 89.21, cont: 81.80, mens24: 1777.90, mens151: 1912.76 },
{ cat: "Autre assistant·e", r1_7: 87.78, r8_16: 81.44, cont: 79.60, mens24: 1777.90, mens151: 1861.37 },
];
const musoData = [
{ cat: "Chef d'orchestre", r1_7: 259.18, r8_16: 212.05, r16p: 182.60, mens30: 3652.04, mens151: 3769.85 },
{ cat: "Musicien·ne", r1_7: 174.36, r8_16: 153.25, r16p: 134.90, mens30: 2968.48, mens151: 3063.00 },
{ cat: "Musicien·ne d'orchestre < 10 musiciens & chœurs", r1_7: 174.36, r8_16: 153.25, r16p: 134.90, mens30: 2968.48, mens151: 3063.00 },
{ cat: "Musicien·ne d'orchestre > 10 musiciens & chœurs", r1_7: 129.88, r8_16: 129.88, r16p: 129.88, mens30: 2604.62, mens151: 2709.58 },
{ cat: "Chœurs d'orchestre", r1_7: 129.88, r8_16: 129.88, r16p: 129.88, mens30: 2604.62, mens151: 2709.58 },
];
const techData = [
{ cls: "Cadres", h200: 14.73, m200: 2234.10, h500: 18.78, m500: 2848.36, hp: 23.34, mp: 3539.98 },
{ cls: "Agents de maîtrise", h200: 14.24, m200: 2159.78, h500: 15.44, m500: 2341.78, hp: 18.78, mp: 2848.36 },
{ cls: "Employés qualifiés", h200: 12.95, m200: 1964.13, h500: 12.95, m500: 1964.13, hp: 14.96, mp: 2268.98 },
{ cls: "Employés", h200: 12.09, m200: 1833.69, h500: 12.09, m500: 1833.69, hp: 12.66, mp: 1920.14 },
];
const techJobs = [
// CADRES
{ cls: "Cadres", name: "Directeur·trice technique", keywords: ["directeur technique", "directrice technique", "direction technique"] },
{ cls: "Cadres", name: "Régisseur·euse général·e", keywords: ["régisseur général", "régisseuse générale"] },
{ cls: "Cadres", name: "Décorateur·trice", keywords: ["décorateur", "décoratrice"] },
{ cls: "Cadres", name: "Scénographe", keywords: ["scénographe"] },
{ cls: "Cadres", name: "Concepteur·trice du son", keywords: ["concepteur son", "conceptrice son", "sound designer"] },
{ cls: "Cadres", name: "Ingénieur·e du son", keywords: ["ingénieur du son", "ingénieure du son", "ingé son"] },
{ cls: "Cadres", name: "Concepteur·trice lumière/éclairagiste", keywords: ["concepteur lumière", "conceptrice lumière", "éclairagiste", "lighting designer"] },
{ cls: "Cadres", name: "Réalisateur·trice lumière", keywords: ["réalisateur lumière", "réalisatrice lumière"] },
{ cls: "Cadres", name: "Ingénieur·e du son-vidéo", keywords: ["ingénieur son vidéo", "ingénieure son vidéo", "ingénieur audiovisuel", "ingé av"] },
{ cls: "Cadres", name: "Chef opérateur·trice", keywords: ["chef opérateur", "cheffe opératrice", "directeur photo", "directrice photo"] },
{ cls: "Cadres", name: "Réalisateur·trice pour diffusion intégrée au spectacle", keywords: ["réalisateur pour diffusion intégrée au spectacle", "réalisatrice pour diffusion intégrée au spectacle"] },
// AGENTS DE MAÎTRISE
{ cls: "Agents de maîtrise", name: "Régisseur·euse", keywords: ["régisseur", "régisseuse"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse d'orchestre", keywords: ["régisseur d'orchestre", "régisseuse d'orchestre"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse de production", keywords: ["régisseur de production", "régisseuse de production"] },
{ cls: "Agents de maîtrise", name: "Conseiller·e technique effets spéciaux", keywords: ["conseiller technique fx", "conseillère technique fx", "effets spéciaux"] },
{ cls: "Agents de maîtrise", name: "Concepteur·trice artificier·ère", keywords: ["concepteur artificier", "conceptrice artificière", "artificier", "artificière"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse plateau", keywords: ["régisseur plateau", "régisseuse plateau"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse son", keywords: ["régisseur son", "régisseuse son"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse lumière", keywords: ["régisseur lumière", "régisseuse lumière"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse de scène", keywords: ["régisseur de scène", "régisseuse de scène"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse de chœur", keywords: ["régisseur de chœur", "régisseuse de chœur", "regisseur de choeur"] },
{ cls: "Agents de maîtrise", name: "Opérateur·trice son", keywords: ["opérateur son", "opératrice son", "operator son"] },
{ cls: "Agents de maîtrise", name: "Preneur·euse de son", keywords: ["preneur de son", "preneuse de son", "prise de son"] },
{ cls: "Agents de maîtrise", name: "Technicien·ne console", keywords: ["technicien console", "technicienne console"] },
{ cls: "Agents de maîtrise", name: "Sonorisateur·trice", keywords: ["sonorisateur", "sonorisatrice", "sono"] },
{ cls: "Agents de maîtrise", name: "Monteur·euse son", keywords: ["monteur son", "monteuse son", "editing son"] },
{ cls: "Agents de maîtrise", name: "Pupitreur·euse", keywords: ["pupitreur", "pupitreuse"] },
{ cls: "Agents de maîtrise", name: "Chef électricien·ne", keywords: ["chef électricien", "cheffe électricienne"] },
{ cls: "Agents de maîtrise", name: "Technicien·ne CAO-PAO", keywords: ["technicien cao", "technicienne cao", "technicien pao", "dao"] },
{ cls: "Agents de maîtrise", name: "Opérateur·trice lumière", keywords: ["opérateur lumière", "opératrice lumière"] },
{ cls: "Agents de maîtrise", name: "Chef machiniste", keywords: ["chef machiniste", "cheffe machiniste"] },
{ cls: "Agents de maîtrise", name: "Chef monteur·euse de structures", keywords: ["chef monteur structures", "cheffe monteuse structures"] },
{ cls: "Agents de maîtrise", name: "Ensemblier·ère de spectacle", keywords: ["ensemblier", "ensemblière"] },
{ cls: "Agents de maîtrise", name: "Cadreur·euse", keywords: ["cadreur", "cadreuse", "camera operator"] },
{ cls: "Agents de maîtrise", name: "Monteur·euse", keywords: ["monteur", "monteuse", "editor"] },
{ cls: "Agents de maîtrise", name: "Opérateur·trice image", keywords: ["opérateur image", "opératrice image"] },
{ cls: "Agents de maîtrise", name: "Opérateur·trice vidéo", keywords: ["opérateur vidéo", "opératrice vidéo"] },
{ cls: "Agents de maîtrise", name: "Régisseur·euse audiovisuel·le", keywords: ["régisseur audiovisuel", "régisseuse audiovisuelle"] },
{ cls: "Agents de maîtrise", name: "Chef de la sécurité", keywords: ["chef sécurité", "responsable sécurité"] },
{ cls: "Agents de maîtrise", name: "Réalisateur·trice son", keywords: ["réalisateur son", "réalisatrice son"] },
// EMPLOYÉS QUALIFIÉS
{ cls: "Employés qualifiés", name: "Régisseur·euse adjoint·e", keywords: ["régisseur adjoint", "régisseuse adjointe"] },
{ cls: "Employés qualifiés", name: "Technicien·ne pyrotechnie", keywords: ["technicien pyrotechnie", "technicienne pyrotechnie", "pyro"] },
{ cls: "Employés qualifiés", name: "Technicien·ne effets spéciaux", keywords: ["technicien fx", "technicienne fx", "effets spéciaux"] },
{ cls: "Employés qualifiés", name: "Artificier·ère", keywords: ["artificier", "artificière"] },
{ cls: "Employés qualifiés", name: "Technicien·ne son", keywords: ["technicien son", "technicienne son"] },
{ cls: "Employés qualifiés", name: "Technicien·ne instruments", keywords: ["technicien instruments", "technicienne instruments", "backliner"] },
{ cls: "Employés qualifiés", name: "Accordeur·euse", keywords: ["accordeur", "accordeuse"] },
{ cls: "Employés qualifiés", name: "Électricien·ne", keywords: ["électricien", "électricienne"] },
{ cls: "Employés qualifiés", name: "Technicien·ne lumière", keywords: ["technicien lumière", "technicienne lumière"] },
{ cls: "Employés qualifiés", name: "Accessoiriste", keywords: ["accessoiriste"] },
{ cls: "Employés qualifiés", name: "Accessoiriste-constructeur·trice", keywords: ["accessoiriste constructeur", "accessoiriste constructrice"] },
{ cls: "Employés qualifiés", name: "Accrocheur·euse-rigger", keywords: ["accrocheur", "accrocheuse", "rigger"] },
{ cls: "Employés qualifiés", name: "Assistant·e décorateur·trice", keywords: ["assistant décorateur", "assistante décoratrice"] },
{ cls: "Employés qualifiés", name: "Cintrier·ière", keywords: ["cintrier", "cintrière"] },
{ cls: "Employés qualifiés", name: "Constructeur·trice décors", keywords: ["constructeur décors", "constructrice décors"] },
{ cls: "Employés qualifiés", name: "Machiniste", keywords: ["machiniste"] },
{ cls: "Employés qualifiés", name: "Menuisier·ère", keywords: ["menuisier", "menuisière"] },
{ cls: "Employés qualifiés", name: "Peintre décorateur·trice", keywords: ["peintre décorateur", "peintre décoratrice"] },
{ cls: "Employés qualifiés", name: "Serrurier·ière", keywords: ["serrurier", "serrurière"] },
{ cls: "Employés qualifiés", name: "Staffeur·euse", keywords: ["staffeur", "staffeuse"] },
{ cls: "Employés qualifiés", name: "Tapissier·ière", keywords: ["tapissier", "tapissière"] },
{ cls: "Employés qualifiés", name: "Technicien·ne de plateau", keywords: ["technicien de plateau", "technicienne de plateau"] },
{ cls: "Employés qualifiés", name: "Technicien·ne structures", keywords: ["technicien structures", "technicienne structures"] },
{ cls: "Employés qualifiés", name: "Monteur·euse de spectacle", keywords: ["monteur de spectacle", "monteuse de spectacle"] },
{ cls: "Employés qualifiés", name: "Technicien·ne hydraulique", keywords: ["technicien hydraulique", "technicienne hydraulique"] },
{ cls: "Employés qualifiés", name: "Technicien·ne vidéo", keywords: ["technicien vidéo", "technicienne vidéo"] },
{ cls: "Employés qualifiés", name: "Projectionniste", keywords: ["projectionniste"] },
{ cls: "Employés qualifiés", name: "Technicien·ne prompteur·euse", keywords: ["technicien prompteur", "technicienne prompteuse", "prompteur", "prompteuse"] },
// EMPLOYÉS
{ cls: "Employés", name: "Technicien·ne groupe électrogène", keywords: ["technicien groupe électrogène", "technicienne groupe électrogène", "groupe électrogène"] },
{ cls: "Employés", name: "Prompteur·euse", keywords: ["prompteur", "prompteuse"] },
{ cls: "Employés", name: "Souffleur·euse", keywords: ["souffleur", "souffleuse"] },
{ cls: "Employés", name: "Poursuiteur·euse", keywords: ["poursuiteur", "poursuiteuse"] },
{ cls: "Employés", name: "Peintre", keywords: ["peintre"] },
{ cls: "Employés", name: "Cariste", keywords: ["cariste", "chariot élévateur"] },
{ cls: "Employés", name: "Agent·e de sécurité", keywords: ["agent de sécurité", "agente de sécurité", "sécurité"] },
];
export default function Annexe1Content() {
const [searchTerm, setSearchTerm] = useState('');
const [filteredJobs, setFilteredJobs] = useState<typeof techJobs>([]);
const normalize = (s: string) => {
return s.toLowerCase()
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
.replace(/œ/g, "oe")
.replace(/[·''`^~\-_/.,:;()]/g, " ")
.replace(/\s+/g, " ")
.trim();
};
const matchJob = (job: typeof techJobs[0], query: string) => {
if (!query) return true;
const nq = normalize(query);
if (normalize(job.name).includes(nq)) return true;
return job.keywords.some(kw => normalize(kw).includes(nq));
};
useEffect(() => {
setFilteredJobs(techJobs.filter(job => matchJob(job, searchTerm)));
}, [searchTerm]);
const groupedJobs = filteredJobs.reduce((acc, job) => {
if (!acc[job.cls]) acc[job.cls] = [];
acc[job.cls].push(job);
return acc;
}, {} as Record<string, typeof techJobs>);
const highlightRow = (cls: string) => {
const rows = document.querySelectorAll('#a1-tech-body tr');
rows.forEach((r: Element) => {
const td0 = (r as HTMLTableRowElement).cells[0];
if (td0 && td0.textContent?.trim().indexOf(cls) === 0) {
r.classList.add('hl');
r.scrollIntoView({ behavior: 'smooth', block: 'center' });
setTimeout(() => r.classList.remove('hl'), 2000);
}
});
};
return (
<div className="space-y-6">
<div className="rounded-xl border bg-gradient-to-br from-slate-50 to-slate-100 p-4">
<h2 className="text-lg font-semibold text-slate-900 mb-1">
Annexe 1 - Exploitants de lieux, producteurs ou diffuseurs de spectacles dramatiques, lyriques, chorégraphiques et de musique classique
</h2>
<p className="text-sm text-slate-600">
Minima pour les artistes dramatiques, lyriques, chorégraphiques, musiciens et techniciens des théâtres
</p>
</div>
<div className="ccnsvp-grid">
{/* ARTISTES THÉÂTRE */}
<div className="rounded-xl border bg-white p-5">
<h3 className="text-base font-bold text-slate-900 mb-3">Artistes dramatiques Théâtre</h3>
<div className="ccnsvp-kpi">
<div className="lbl">Cachet de répétition</div>
<div className="num">83 / jour</div>
<div className="muted">Soit 1 service unique de 4h, soit 2 services d'une durée cumulée de 6h</div>
</div>
<div className="font-semibold text-sm text-slate-700 mb-2">Cachet de représentation</div>
<p className="text-xs text-slate-500 mb-3">
Cachets selon capacité de salle et nombre de représentations sur une période de 3 mois ou 90j calendaires.
Colonne « Mensuel » = 24 rep./mois.
</p>
<table className="ccnsvp-table">
<thead>
<tr>
<th>Capacité de salle</th>
<th>Jusqu'à 24</th>
<th>25 à 48</th>
<th>Plus de 48</th>
<th>Mensuel</th>
</tr>
</thead>
<tbody>
{theatreData.map((row, i) => (
<tr key={i}>
<td dangerouslySetInnerHTML={{ __html: row.cap }}></td>
<td>{euro(row.j24)}</td>
<td>{euro(row.j25_48)}</td>
<td>{euro(row.j48p)}</td>
<td>{euro(row.mens)}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* THÉÂTRE MUSICAL */}
<div className="rounded-xl border bg-white p-5 md:row-span-2">
<h3 className="text-base font-bold text-slate-900 mb-3">
Théâtre musical Comédie musicale Opérette Autres spectacles
</h3>
<div className="ccnsvp-kpi">
<div className="lbl">Service de répétition</div>
<div className="num">46,60 / service</div>
<div className="muted">Pour tous les artistes hors musiciens.</div>
<div className="muted mb-2">
1 service = 4h pour les artistes dramatiques et lyriques ; 3h pour les artistes chorégraphiques
</div>
<div className="ccnsvp-alert">
<span className="icon"></span>
<span>
Un service de répétition payé <b>46,60</b> pour <b>4h</b> est inférieur au
<b> SMIC</b> (11,65 brut horaire au lieu de 11,88). Dans ce cas, il convient d'appliquer au minimum le SMIC, soit 47,52€.
</span>
</div>
</div>
<p className="text-xs text-slate-500 mb-3">
Cachets par représentation et minima mensuels (24 rep. et 151h67).
</p>
<div className="overflow-x-auto">
<table className="ccnsvp-table text-xs">
<thead>
<tr>
<th>Profession</th>
<th>1 à 7</th>
<th>8 à 16</th>
<th>Exploitation continue</th>
<th>Mensuel (24 rep.)</th>
<th>Mensuel (151h67)</th>
</tr>
</thead>
<tbody>
{musicalData.map((row, i) => (
<tr key={i}>
<td className="font-medium">{row.cat}</td>
<td>{euro(row.r1_7)}</td>
<td>{euro(row.r8_16)}</td>
<td>{euro(row.cont)}</td>
<td>{euro(row.mens24)}</td>
<td>{euro(row.mens151)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* MUSICIENS */}
<div className="rounded-xl border bg-white p-5">
<h3 className="text-base font-bold text-slate-900 mb-3">Artistes musiciens & orchestre</h3>
<div className="ccnsvp-kpi">
<div className="lbl">Cachet de répétition</div>
<div className="num">104,94€/cachet</div>
<div className="muted">Pour 2 services de 3h chacun sur la journée.</div>
<div className="num mt-2">72,95€/cachet</div>
<div className="muted">Pour 1 service isolé de 3h sur la journée.</div>
</div>
<div className="font-semibold text-sm text-slate-700 mb-2">Cachet de représentation</div>
<p className="text-xs text-slate-500 mb-3">
Cachets par représentation + minima mensuels (30 rep. et 151h67).
</p>
<table className="ccnsvp-table">
<thead>
<tr>
<th>Catégorie</th>
<th>1 à 7</th>
<th>8 à 16</th>
<th>Plus de 16</th>
<th>Mensuel (30 rep.)</th>
<th>Mensuel (151h67)</th>
</tr>
</thead>
<tbody>
{musoData.map((row, i) => (
<tr key={i}>
<td className="font-medium" dangerouslySetInnerHTML={{ __html: row.cat }}></td>
<td>{euro(row.r1_7)}</td>
<td>{euro(row.r8_16)}</td>
<td>{euro(row.r16p)}</td>
<td>{euro(row.mens30)}</td>
<td>{euro(row.mens151)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* TECHNICIENS */}
<div className="rounded-xl border bg-white p-5">
<h3 className="text-base font-bold text-slate-900 mb-2">Techniciens — Théâtres</h3>
<p className="text-sm text-slate-600 mb-4">
Minima horaires et mensuels selon jauge. Vous pouvez <b>rechercher</b> une profession ci-dessous et trouver sa classification.
La liste des professions est exhaustive de ce qui est prévu par l'Annexe 1.
</p>
<div className="ccnsvp-search">
<input
type="search"
placeholder="Rechercher une profession (ex. régisseur plateau, chef machiniste, électricien…)"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1"
/>
<span className="ccnsvp-badge">
{filteredJobs.length} résultat{filteredJobs.length > 1 ? 's' : ''}
</span>
</div>
<div className="overflow-x-auto">
<table className="ccnsvp-table">
<thead>
<tr>
<th>Classification</th>
<th>Horaire 200 pl.</th>
<th>Mensuel 200 pl.</th>
<th>Horaire 201500</th>
<th>Mensuel 201500</th>
<th>Horaire &gt; 500</th>
<th>Mensuel &gt; 500</th>
</tr>
</thead>
<tbody id="a1-tech-body">
{techData.map((row, i) => (
<tr key={i}>
<td><b>{row.cls}</b></td>
<td>{euro(row.h200)}/h</td>
<td>{euro(row.m200)}</td>
<td>{euro(row.h500)}/h</td>
<td>{euro(row.m500)}</td>
<td>{euro(row.hp)}/h</td>
<td>{euro(row.mp)}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Accordéons */}
{Object.keys(groupedJobs).length > 0 && (
<div className="ccnsvp-acc mt-4">
{Object.entries(groupedJobs).map(([cls, jobs]) => (
<details key={cls}>
<summary>
{cls} <span className="ccnsvp-badge ml-2">({jobs.length})</span>
</summary>
<ul>
{jobs.sort((a, b) => a.name.localeCompare(b.name, 'fr')).map((job, i) => (
<li
key={i}
onClick={() => highlightRow(cls)}
title={job.keywords.join(', ')}
>
{job.name}
</li>
))}
</ul>
</details>
))}
</div>
)}
</div>
</div>
);
}