Implement UI enhancements for participant import without transaction in MSIN-4482. Add new styles for import dropzone and integrate import functionality into event management panel. Update navigation for improved access to import features. Increment version code.
This commit is contained in:
@ -11,26 +11,26 @@ Doc de cadrage — source de vérité pour le développement de ce chantier.
|
||||
|
||||
## Intention
|
||||
|
||||
Utiliser la suite MS1 (gestion, ChronoTrack, hub allégé, signature de dossier, accès par tâche) pour des clients **sans** tunnel d’inscription / paiement en ligne MS1.
|
||||
Les participants arrivent par **import fichier** et/ou **saisie rapide** ; ensuite sync ChronoTrack.
|
||||
Utiliser la suite MS1 (gestion, ChronoTrack, outils promoteur existants) pour des clients **sans** tunnel d’inscription / paiement en ligne MS1.
|
||||
Les participants arrivent surtout par **import fichier** (puis saisie rapide plus tard) ; ensuite sync ChronoTrack.
|
||||
|
||||
Aussi utile le **jour J** sur une course **avec** inscription en ligne : ajout / correction rapide (sexe, âge, etc.) **sans** transaction financière.
|
||||
Aussi utile plus tard le **jour J** sur une course **avec** inscription en ligne : correction / ajout rapide **sans** transaction financière.
|
||||
|
||||
## Décisions actées
|
||||
|
||||
| Sujet | Décision |
|
||||
|---|---|
|
||||
| Source terrain | Toujours **`resultats_*`** (`resultats_participants`, `resultats_epreuves_commandees`, `resultats_questions`) — pas le panier |
|
||||
| Source terrain | Toujours **`resultats_*`** — **pas de panier / `no_panier`** pour ces lignes |
|
||||
| Type événement | Nouveau type / mode « sans inscription en ligne » (ex. timing) — à créer |
|
||||
| Paiement MS1 | Hors scope pour ces lignes ; **interdit** le « panier à 0 $ » |
|
||||
| Clé unique | ID du fichier client = **`external_id` ChronoTrack** au push (pas seulement `par_id_original`) ; même fichier = fallback import CT |
|
||||
| Questions import | **Option C** : matcher les questions existantes ; non matchées → preview + **création confirmée** |
|
||||
| Hub | **Mini-hub / hub allégé** (pas zéro hub) ; grossit avec les options |
|
||||
| Opérateur | MS1 (Stéphane / équipe) = promoteur opérateur ; délégation = **kits limités par tâche**, pas kit promoteur client « total » |
|
||||
| Signature de dossier | **Dans le scope hub** — à cartographier (module exact) ; **≠ waiver** |
|
||||
| Waiver | **Hors scope** — ne pas assimiler « signature de dossier » au waiver |
|
||||
| Édition fiches | Create/edit sur `resultats_participants` (+ questions) ; **même** chemin jour J sur événement en ligne |
|
||||
| Finances | Marqueur d’**origine** obligatoire ; import / saisie rapide **exclus** des rapports paiement / facture |
|
||||
| Paiement MS1 | Hors scope ; **interdit** le « panier à 0 $ » |
|
||||
| Clé unique | ID du fichier client = **`external_id` ChronoTrack** au push ; même fichier = fallback import CT |
|
||||
| Questions import | **Option C** : matcher ; non matchées → preview + **création confirmée** |
|
||||
| Hub promoteur | **Ne pas toucher pour l’instant** — hub actuel tel quel |
|
||||
| Qui est promoteur | Opérateur MS1 se met **lui-même** promoteur ; droits clients = kits **minimisés via l’existant** |
|
||||
| Mini-hub | **Plus tard**, au fur et à mesure — pas un livrable immédiat |
|
||||
| Waiver | **Hors scope** |
|
||||
| Édition / saisie rapide | Après l’import (phase suivante) |
|
||||
| Finances | Marqueur d’**origine** ; import / saisie rapide exclus des rapports paiement |
|
||||
|
||||
## Origines participant (marqueur)
|
||||
|
||||
@ -38,36 +38,60 @@ Aussi utile le **jour J** sur une course **avec** inscription en ligne : ajout /
|
||||
|---|---|
|
||||
| `inscription_en_ligne` | Flux payant actuel |
|
||||
| `import_fichier` | Bulk modèle fichier |
|
||||
| `saisie_rapide` | Création unitaire / jour J |
|
||||
| `saisie_rapide` | Création unitaire / jour J (plus tard) |
|
||||
|
||||
## Ancrages code existants
|
||||
## Phases (réalignées)
|
||||
|
||||
- Sync CT : `php/chronotrack_api/` — push MS1→CT ; aujourd’hui `external_id` ≈ `par_id_original` → **à adapter** si clé import présente
|
||||
- `ct_entry_id` : ID interne CT post-sync (MSIN-4328) — distinct de la clé fichier
|
||||
- Questions → CT : `entry_note_*` (MSIN-4461) via `resultats_questions` + `inscriptions_questions`
|
||||
- Hub / accès : `php/inc_fx_promoteur_hub.php`, `inscriptions_eve_acces*`
|
||||
- Pas d’import générique participants aujourd’hui (seulement FNQ/TQ/PayPal spécialisés)
|
||||
1. **UI import superadm** (en cours) — dropzone, mapping colonnes + épreuves, templates persistants
|
||||
2. **Écriture `resultats_*`** — upsert clé unique + questions C (sans panier)
|
||||
3. **ChronoTrack** — push avec `par_external_id`
|
||||
4. **Saisie / édition** — fiche rapide (+ jour J)
|
||||
5. **Hub minimisé** — seulement si/quand nécessaire
|
||||
|
||||
## Hors scope
|
||||
## UI import (décisions)
|
||||
|
||||
- Waiver / `waiver_cleared` comme livrable de ce projet
|
||||
- Kit promoteur client complet sur ce type d’événement
|
||||
- Facturation / remboursements pour lignes import / saisie rapide
|
||||
- Import inverse ChronoTrack → MS1 comme chemin nominal
|
||||
- Emplacement v1 : **superadm fiche événement** (panneau sous ChronoTrack ; ancre « Import »)
|
||||
- Colonne fichier pour l’épreuve → match vers `epr_id` de l’événement
|
||||
- UX : dropzone ludique + mapping guidé
|
||||
- Template mapping : table `inscriptions_import_templates` (empreinte en-têtes + eve/com)
|
||||
|
||||
## Phases
|
||||
## Exemple fichier client (référence)
|
||||
|
||||
0. **Cadrage** — contrat fichier + unique ID ; stratégie questions C ; carto « signature de dossier » ; inventaire `no_panier` / finances
|
||||
1. **Fondations** — type événement ; colonnes clé externe + origine ; coquille `pec` sans commande payante
|
||||
2. **Import** — UI + upsert clé unique + questions (match / création confirmée) → `resultats_*`
|
||||
3. **Saisie / édition** — fiche rapide ; ouvert aussi sur courses en ligne (jour J)
|
||||
4. **CT + hub** — sync avec clé fichier ; mini-hub ; accès par tâche ; signature de dossier
|
||||
5. **Ops** — tests mélange payant vs hors-transaction ; filet même fichier → CT
|
||||
Fichier type : export ~Échappée (en-têtes déjà normalisés ; l’original client peut différer).
|
||||
|
||||
## Règles agent (pendant le chantier)
|
||||
| Colonne exemple | Cible MS1 |
|
||||
|---|---|
|
||||
| Attendee # | `par_external_id` (clé unique / CT) |
|
||||
| First Name / Last Name | `par_prenom` / `par_nom` |
|
||||
| Ticket Type | `epr` (valeurs → épreuves MS1) |
|
||||
| No dossard | `no_bib` |
|
||||
| Gender (Male/Female/Other) | `par_sexe` (normaliser à l’écriture) |
|
||||
| Age / Birth Date | `par_age` / `par_naissance` |
|
||||
| Nationalité, urgence, allergies, condition médicale, langue… | questions (option C) |
|
||||
|
||||
- Changement minimal ; référencer **MSIN-4482** dans SQL / commentaires PHP.
|
||||
- Ne pas brancher ces flux sur le panier / paiement.
|
||||
- Ne pas réintroduire le waiver « pour faire la signature de dossier ».
|
||||
- Préférer étendre gestion / hub existants plutôt que doubler les fiches participant.
|
||||
- SQL : uniquement **dev préprod**, puis Navicat + sync outil (workflow projet).
|
||||
~1800 lignes, ~10–12 valeurs distinctes de Ticket Type (distances / jours).
|
||||
|
||||
## Fichiers
|
||||
|
||||
| Rôle | Fichier |
|
||||
|---|---|
|
||||
| SQL | `sql/MSIN-4482-import-resultats-templates.sql` |
|
||||
| Logique + UI | `php/inc_fx_import_resultats.php` |
|
||||
| AJAX | `superadm/ajax_import_resultats.php` |
|
||||
| Branche fiche eve | `superadm/php/inc_form.php` |
|
||||
| CSS | `superadm/css/style_blue.css` |
|
||||
|
||||
## Hors scope (maintenant)
|
||||
|
||||
- Hub promoteur / mini-hub
|
||||
- Waiver
|
||||
- Écriture `resultats_*` (prochaine étape après UI)
|
||||
- Import inverse CT → MS1
|
||||
|
||||
## Règles agent
|
||||
|
||||
- Changement minimal ; référencer **MSIN-4482**.
|
||||
- **Pas de panier** pour ces participants — écrire dans `resultats_*`.
|
||||
- **Ne pas modifier le hub promoteur** tant que ce n’est pas redemandé.
|
||||
- Ne pas inventer de waiver pour « signature de dossier ».
|
||||
- SQL : **dev préprod** seulement, puis Navicat + sync outil.
|
||||
|
||||
1083
php/inc_fx_import_resultats.php
Normal file
1083
php/inc_fx_import_resultats.php
Normal file
File diff suppressed because it is too large
Load Diff
63
sql/MSIN-4482-import-resultats-templates.sql
Normal file
63
sql/MSIN-4482-import-resultats-templates.sql
Normal file
@ -0,0 +1,63 @@
|
||||
-- MSIN-4482 — Templates mapping import participants (hors transaction)
|
||||
-- Contexte : UI import fiche événement superadm ; upsert sur clé fichier
|
||||
-- Notes : exécution UNIQUEMENT sur dev préprod ; autres env = Navicat structure + sync_static_db
|
||||
|
||||
SET @db := DATABASE();
|
||||
|
||||
-- Templates de mapping colonnes (réutilisables par empreinte d'en-têtes)
|
||||
CREATE TABLE IF NOT EXISTS `inscriptions_import_templates` (
|
||||
`tpl_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`eve_id` int(10) unsigned NOT NULL DEFAULT 0,
|
||||
`com_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'MSIN-4482 — 0 = lié événement ; >0 = réutilisable côté client',
|
||||
`tpl_name` varchar(120) NOT NULL DEFAULT '',
|
||||
`tpl_header_sig` char(40) NOT NULL DEFAULT '' COMMENT 'sha1 en-têtes normalisés',
|
||||
`tpl_headers_json` mediumtext NOT NULL,
|
||||
`tpl_mapping_json` mediumtext NOT NULL COMMENT 'colonnes + valeurs épreuve + questions',
|
||||
`tpl_actif` tinyint(1) unsigned NOT NULL DEFAULT 1,
|
||||
`tpl_usa_id` int(10) unsigned NOT NULL DEFAULT 0,
|
||||
`tpl_creation` datetime NOT NULL,
|
||||
`tpl_maj` datetime NOT NULL,
|
||||
PRIMARY KEY (`tpl_id`),
|
||||
KEY `idx_import_tpl_eve_sig` (`eve_id`, `tpl_header_sig`),
|
||||
KEY `idx_import_tpl_com_sig` (`com_id`, `tpl_header_sig`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Clé unique fichier (= external_id ChronoTrack) + origine d'entrée
|
||||
SET @col_ext := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'resultats_participants' AND COLUMN_NAME = 'par_external_id'
|
||||
);
|
||||
SET @sql_ext := IF(
|
||||
@col_ext = 0,
|
||||
'ALTER TABLE resultats_participants ADD COLUMN par_external_id VARCHAR(64) NULL DEFAULT NULL COMMENT ''MSIN-4482 — clé unique import / CT external_id'' AFTER par_id_original',
|
||||
'SELECT ''par_external_id already exists'' AS note'
|
||||
);
|
||||
PREPARE stmt_ext FROM @sql_ext;
|
||||
EXECUTE stmt_ext;
|
||||
DEALLOCATE PREPARE stmt_ext;
|
||||
|
||||
SET @col_ori := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'resultats_participants' AND COLUMN_NAME = 'par_origine'
|
||||
);
|
||||
SET @sql_ori := IF(
|
||||
@col_ori = 0,
|
||||
'ALTER TABLE resultats_participants ADD COLUMN par_origine VARCHAR(32) NULL DEFAULT NULL COMMENT ''MSIN-4482 — import_fichier|saisie_rapide|inscription_en_ligne'' AFTER par_external_id',
|
||||
'SELECT ''par_origine already exists'' AS note'
|
||||
);
|
||||
PREPARE stmt_ori FROM @sql_ori;
|
||||
EXECUTE stmt_ori;
|
||||
DEALLOCATE PREPARE stmt_ori;
|
||||
|
||||
SET @idx_ext := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'resultats_participants' AND INDEX_NAME = 'idx_par_external_eve'
|
||||
);
|
||||
SET @sql_idx := IF(
|
||||
@idx_ext = 0,
|
||||
'ALTER TABLE resultats_participants ADD KEY idx_par_external_eve (eve_id, par_external_id)',
|
||||
'SELECT ''idx_par_external_eve already exists'' AS note'
|
||||
);
|
||||
PREPARE stmt_idx FROM @sql_idx;
|
||||
EXECUTE stmt_idx;
|
||||
DEALLOCATE PREPARE stmt_idx;
|
||||
70
superadm/ajax_import_resultats.php
Normal file
70
superadm/ajax_import_resultats.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* MSIN-4482 — AJAX import participants (hors transaction) — superadm.
|
||||
*/
|
||||
require_once dirname(__FILE__) . '/php/inc_functions.php';
|
||||
require_once dirname(__FILE__) . '/../php/inc_fx_import_resultats.php';
|
||||
|
||||
$intUsaId = fxSuperadmAjaxAuthOrJsonExit();
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
$strAction = trim((string)($_REQUEST['a'] ?? ''));
|
||||
$intEveId = intval($_REQUEST['eve_id'] ?? 0);
|
||||
$tabRetour = array('state' => 'error', 'message' => 'action_invalide');
|
||||
|
||||
switch ($strAction) {
|
||||
case 'upload':
|
||||
if ($intEveId <= 0) {
|
||||
$tabRetour = array('state' => 'error', 'message' => 'eve_id manquant');
|
||||
break;
|
||||
}
|
||||
if (empty($_FILES['file'])) {
|
||||
$tabRetour = array('state' => 'error', 'message' => 'Fichier manquant');
|
||||
break;
|
||||
}
|
||||
$tabRetour = fxImportResultatsSaveUpload($intEveId, $_FILES['file']);
|
||||
break;
|
||||
|
||||
case 'save_template':
|
||||
if ($intEveId <= 0) {
|
||||
$tabRetour = array('state' => 'error', 'message' => 'eve_id manquant');
|
||||
break;
|
||||
}
|
||||
$strToken = trim((string)($_REQUEST['token'] ?? ''));
|
||||
$strName = trim((string)($_REQUEST['tpl_name'] ?? ''));
|
||||
$arrMapping = $_REQUEST['mapping'] ?? array();
|
||||
if (is_string($arrMapping)) {
|
||||
$arrDecoded = json_decode($arrMapping, true);
|
||||
$arrMapping = is_array($arrDecoded) ? $arrDecoded : array();
|
||||
}
|
||||
if (!is_array($arrMapping)) {
|
||||
$arrMapping = array();
|
||||
}
|
||||
$tabRetour = fxImportResultatsSaveTemplate($intEveId, $strToken, $strName, $arrMapping, $intUsaId, true);
|
||||
break;
|
||||
|
||||
case 'validate':
|
||||
if ($intEveId <= 0) {
|
||||
$tabRetour = array('state' => 'error', 'message' => 'eve_id manquant');
|
||||
break;
|
||||
}
|
||||
$strToken = trim((string)($_REQUEST['token'] ?? ''));
|
||||
$arrMapping = $_REQUEST['mapping'] ?? array();
|
||||
if (is_string($arrMapping)) {
|
||||
$arrDecoded = json_decode($arrMapping, true);
|
||||
$arrMapping = is_array($arrDecoded) ? $arrDecoded : array();
|
||||
}
|
||||
if (!is_array($arrMapping)) {
|
||||
$arrMapping = array();
|
||||
}
|
||||
$tabRetour = fxImportResultatsValidateMapping($intEveId, $strToken, $arrMapping);
|
||||
break;
|
||||
|
||||
default:
|
||||
$tabRetour = array('state' => 'error', 'message' => 'action_invalide');
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode($tabRetour, JSON_UNESCAPED_UNICODE);
|
||||
@ -396,4 +396,76 @@ table.rapport tr {background-color: #190d7394;}*/
|
||||
.superadm-v2-panel__body .custom-control-label {
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* MSIN-4482 — Import participants (hors transaction) */
|
||||
.ms1-import__drop {
|
||||
align-items: center;
|
||||
background: linear-gradient(160deg, #f0f9fc 0%, #e4f3f8 100%);
|
||||
border: 2px dashed #045a6e;
|
||||
border-radius: 12px;
|
||||
color: #045a6e;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
justify-content: center;
|
||||
min-height: 9.5rem;
|
||||
padding: 1.25rem 1rem;
|
||||
text-align: center;
|
||||
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
.ms1-import__drop:hover,
|
||||
.ms1-import__drop:focus {
|
||||
background: #e8f6fb;
|
||||
box-shadow: 0 4px 14px rgba(4, 90, 110, 0.12);
|
||||
outline: none;
|
||||
}
|
||||
.ms1-import__drop.is-drag {
|
||||
background: #dff3f9;
|
||||
border-color: #0284a8;
|
||||
border-style: solid;
|
||||
box-shadow: 0 0 0 3px rgba(2, 132, 168, 0.2);
|
||||
transform: scale(1.01);
|
||||
}
|
||||
.ms1-import__drop.is-busy {
|
||||
cursor: wait;
|
||||
opacity: 0.72;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ms1-import__drop.is-ready {
|
||||
border-style: solid;
|
||||
min-height: 4.5rem;
|
||||
padding: 0.65rem;
|
||||
}
|
||||
.ms1-import__drop.is-ready .ms1-import__drop-sub {
|
||||
display: none;
|
||||
}
|
||||
.ms1-import__drop-icon {
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.ms1-import__drop-title {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.ms1-import__drop-sub {
|
||||
color: #64748b;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.ms1-import__h {
|
||||
color: #045a6e;
|
||||
font-weight: 700;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
.ms1-import__status.alert {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.ms1-import__epr,
|
||||
.ms1-import__que {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 0.85rem;
|
||||
}
|
||||
@ -128,6 +128,10 @@ if ($strAction == 'mod') {
|
||||
));
|
||||
fxChronotrackApiAdminRenderEventPage($intEveIdForm, $strLangue, true, true);
|
||||
fxSuperadmV2PanelClose(array('collapse_id' => 'chronotrackApiCollapse'));
|
||||
|
||||
// MSIN-4482 — Import participants hors transaction (UI + templates)
|
||||
require_once(dirname(__FILE__) . '/../../php/inc_fx_import_resultats.php');
|
||||
fxImportResultatsRenderEventPanel($intEveIdForm);
|
||||
}
|
||||
|
||||
}
|
||||
@ -153,6 +157,10 @@ if ($strAction == 'mod') {
|
||||
onclick="if (window.jQuery) { jQuery('#chronotrackApiCollapse').collapse('show'); }">
|
||||
ChronoTrack
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="#import_resultats"
|
||||
onclick="if (window.jQuery) { jQuery('#importResultatsCollapse').collapse('show'); }">
|
||||
Import
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="#list_eve_extra">
|
||||
Champs supplémentaires
|
||||
</a>
|
||||
|
||||
Reference in New Issue
Block a user