gift revamp
This commit is contained in:
+334
-385
@@ -4,37 +4,21 @@
|
|||||||
require __DIR__ . '/includes/auth.php';
|
require __DIR__ . '/includes/auth.php';
|
||||||
require_login('/login.php');
|
require_login('/login.php');
|
||||||
require __DIR__ . '/includes/db.php';
|
require __DIR__ . '/includes/db.php';
|
||||||
require_once __DIR__ . '/includes/i18n.php'; // Toujours s'assurer que tr() est dispo
|
require_once __DIR__ . '/includes/i18n.php';
|
||||||
|
|
||||||
if (session_status() === PHP_SESSION_NONE) { session_start(); }
|
if (session_status() === PHP_SESSION_NONE) { session_start(); }
|
||||||
|
|
||||||
// --- 1. CONFIGURATION & DONNÉES ---
|
// --- 1. CONFIGURATION & DONNÉES ---
|
||||||
|
|
||||||
$year = (int)date('Y');
|
$year = (int)date('Y');
|
||||||
$pageTitle = tr('gift_page_title');
|
$pageTitle = tr('gift_page_title');
|
||||||
$activePage = "gift-list";
|
$activePage = "gift-list";
|
||||||
$bodyClass = "pf-gift-list";
|
$bodyClass = "pf-gift-list";
|
||||||
$pageCss = "/modules/gift-list/gift-list.css";
|
$pageCss = "/modules/gift-list/gift-list.css";
|
||||||
|
|
||||||
// Personnes
|
|
||||||
$baseAdults = ['Laia', 'Laura', 'Avi Iaia'];
|
$baseAdults = ['Laia', 'Laura', 'Avi Iaia'];
|
||||||
$children = ['Pol', 'Pep', 'Elna', 'Bru', 'Guim'];
|
$children = ['Pol', 'Pep', 'Elna', 'Bru', 'Guim'];
|
||||||
|
|
||||||
// Configuration des Vues
|
|
||||||
$VIEWS = [
|
|
||||||
'nadal' => ['TIO', 'NOEL', 'ROIS'],
|
|
||||||
'anniversary' => ['ANNIV', 'SANT'],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Vue courante
|
|
||||||
$currentView = strtolower($_GET['view'] ?? ($_SESSION['gift_view'] ?? 'nadal'));
|
|
||||||
if (!isset($VIEWS[$currentView])) $currentView = 'nadal';
|
|
||||||
$_SESSION['gift_view'] = $currentView;
|
|
||||||
|
|
||||||
$allowedOccasions = $VIEWS[$currentView];
|
|
||||||
|
|
||||||
// Logique spécifique : Adultes supplémentaires pour Anniversaires
|
|
||||||
$extraAdults = ['Pauline', 'Papy JC', 'Mamy Caro'];
|
$extraAdults = ['Pauline', 'Papy JC', 'Mamy Caro'];
|
||||||
|
|
||||||
$adultsByChildForAnniv = [
|
$adultsByChildForAnniv = [
|
||||||
'Pol' => array_merge($baseAdults, $extraAdults),
|
'Pol' => array_merge($baseAdults, $extraAdults),
|
||||||
'Pep' => array_merge($baseAdults, $extraAdults),
|
'Pep' => array_merge($baseAdults, $extraAdults),
|
||||||
@@ -43,7 +27,17 @@ $adultsByChildForAnniv = [
|
|||||||
'Guim' => $baseAdults,
|
'Guim' => $baseAdults,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Labels & Icônes (Utilisation des clés de traduction pour l'affichage)
|
$VIEWS = [
|
||||||
|
'nadal' => ['TIO', 'NOEL', 'ROIS'],
|
||||||
|
'anniversary' => ['ANNIV', 'SANT'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentView = strtolower($_GET['view'] ?? ($_SESSION['gift_view'] ?? 'nadal'));
|
||||||
|
if (!isset($VIEWS[$currentView])) $currentView = 'nadal';
|
||||||
|
$_SESSION['gift_view'] = $currentView;
|
||||||
|
|
||||||
|
$allowedOccasions = $VIEWS[$currentView];
|
||||||
|
|
||||||
$allOccasionLabels = [
|
$allOccasionLabels = [
|
||||||
'TIO' => tr('gift_occ_tio'),
|
'TIO' => tr('gift_occ_tio'),
|
||||||
'NOEL' => tr('gift_occ_noel'),
|
'NOEL' => tr('gift_occ_noel'),
|
||||||
@@ -60,30 +54,40 @@ $occasionIcons = [
|
|||||||
'SANT' => '/modules/gift-list/assets/img/sant.png',
|
'SANT' => '/modules/gift-list/assets/img/sant.png',
|
||||||
];
|
];
|
||||||
|
|
||||||
$tableGifts = 'pf_gifts';
|
|
||||||
|
|
||||||
// --- 2. RÉCUPÉRATION DES DONNÉES ---
|
// --- 2. RÉCUPÉRATION DES DONNÉES ---
|
||||||
|
|
||||||
$inMarks = implode(',', array_fill(0, count($allowedOccasions), '?'));
|
$inMarks = implode(',', array_fill(0, count($allowedOccasions), '?'));
|
||||||
$sql = "SELECT * FROM {$tableGifts} WHERE year = ? AND occasion IN ($inMarks) ORDER BY adult_name, child_name, occasion, created_at";
|
// Le tri SQL regroupe par Occasion, puis par Enfant, puis par Adulte
|
||||||
|
$sql = "SELECT * FROM pf_gifts WHERE year = ? AND occasion IN ($inMarks) ORDER BY occasion ASC, child_name ASC, adult_name ASC, created_at DESC";
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
$params = array_merge([$year], $allowedOccasions);
|
$stmt->execute(array_merge([$year], $allowedOccasions));
|
||||||
$stmt->execute($params);
|
|
||||||
$gifts = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
$gifts = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||||
|
|
||||||
$byOccasion = [];
|
$dataByOccasion = [];
|
||||||
foreach ($gifts as $gift) {
|
$adultsInView = [];
|
||||||
$byOccasion[$gift['occasion']][$gift['child_name']][$gift['adult_name']][] = $gift;
|
|
||||||
|
foreach ($gifts as $g) {
|
||||||
|
$occ = $g['occasion'];
|
||||||
|
$child = $g['child_name'];
|
||||||
|
$adult = $g['adult_name'];
|
||||||
|
|
||||||
|
// Groupement structuré : Occasion -> Enfant -> Cadeaux
|
||||||
|
$dataByOccasion[$occ][$child]['gifts'][] = $g;
|
||||||
|
|
||||||
|
// Totaux pour les petites pills sous le nom de l'enfant
|
||||||
|
if (!isset($dataByOccasion[$occ][$child]['totals'][$adult])) {
|
||||||
|
$dataByOccasion[$occ][$child]['totals'][$adult] = 0;
|
||||||
|
}
|
||||||
|
$dataByOccasion[$occ][$child]['totals'][$adult] += (float)$g['amount'];
|
||||||
|
|
||||||
|
$adultsInView[$adult] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$allAdultsList = array_keys($adultsInView);
|
||||||
|
sort($allAdultsList);
|
||||||
$occasionsToShow = array_values(array_intersect(array_keys($allOccasionLabels), $allowedOccasions));
|
$occasionsToShow = array_values(array_intersect(array_keys($allOccasionLabels), $allowedOccasions));
|
||||||
|
|
||||||
// --- 3. CALCUL TRICOUNT ---
|
// --- 3. CALCUL TRICOUNT (Bilan financier global de la vue) ---
|
||||||
|
$people = array_values(array_unique(array_merge($baseAdults, array_column($gifts, 'adult_name'), array_column($gifts, 'payer_name'))));
|
||||||
$people = $baseAdults;
|
|
||||||
$adultsInDb = array_column($gifts, 'adult_name');
|
|
||||||
$payersInDb = array_column($gifts, 'payer_name');
|
|
||||||
$people = array_values(array_unique(array_merge($people, $adultsInDb, $payersInDb)));
|
|
||||||
$people = array_filter($people);
|
$people = array_filter($people);
|
||||||
|
|
||||||
$matrix = [];
|
$matrix = [];
|
||||||
@@ -97,9 +101,7 @@ foreach ($gifts as $g) {
|
|||||||
$amt = (float)$g['amount'];
|
$amt = (float)$g['amount'];
|
||||||
|
|
||||||
if ($amt > 0 && $adult && $payer && $adult !== $payer) {
|
if ($amt > 0 && $adult && $payer && $adult !== $payer) {
|
||||||
if (isset($matrix[$adult][$payer])) {
|
$matrix[$adult][$payer] += $amt;
|
||||||
$matrix[$adult][$payer] += $amt;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,421 +112,368 @@ for ($i = 0; $i < $countPeople; $i++) {
|
|||||||
$a = $people[$i];
|
$a = $people[$i];
|
||||||
$b = $people[$j];
|
$b = $people[$j];
|
||||||
$net = $matrix[$a][$b] - $matrix[$b][$a];
|
$net = $matrix[$a][$b] - $matrix[$b][$a];
|
||||||
|
if ($net > 0.01) { $settlements[] = ['from' => $a, 'to' => $b, 'amount' => $net]; }
|
||||||
if ($net > 0.01) {
|
elseif ($net < -0.01) { $settlements[] = ['from' => $b, 'to' => $a, 'amount' => -$net]; }
|
||||||
$settlements[] = ['from' => $a, 'to' => $b, 'amount' => $net];
|
|
||||||
} elseif ($net < -0.01) {
|
|
||||||
$settlements[] = ['from' => $b, 'to' => $a, 'amount' => -$net];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. DÉBUT DU RENDU HTML ---
|
|
||||||
require __DIR__ . '/header.php';
|
require __DIR__ . '/header.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="pf-container cl-view-<?= htmlspecialchars($currentView) ?>">
|
<div class="pf-container cl-view-<?= htmlspecialchars($currentView) ?>">
|
||||||
|
|
||||||
<div class="cl-titlebar">
|
<div class="cl-titlebar">
|
||||||
<h1><?= sprintf(tr('gift_main_title'), htmlspecialchars($year)) ?></h1>
|
<h1><?= sprintf(tr('gift_main_title'), $year) ?></h1>
|
||||||
<div class="cl-view-switch" aria-label="<?= tr('gift_aria_change_view') ?>">
|
<div class="cl-view-switch" aria-label="<?= tr('gift_aria_change_view') ?>">
|
||||||
<a href="?view=nadal" class="cl-view-btn <?= $currentView === 'nadal' ? 'is-active' : '' ?>"><?= tr('gift_view_nadal') ?></a>
|
<a href="?view=nadal" class="cl-view-btn <?= $currentView === 'nadal' ? 'is-active' : '' ?>"><?= tr('gift_view_nadal') ?></a>
|
||||||
<a href="?view=anniversary" class="cl-view-btn <?= $currentView === 'anniversary' ? 'is-active' : '' ?>"><?= tr('gift_view_anniv') ?></a>
|
<a href="?view=anniversary" class="cl-view-btn <?= $currentView === 'anniversary' ? 'is-active' : '' ?>"><?= tr('gift_view_anniv') ?></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="pf-section pf-section--panel">
|
<div class="pf-filter-bar">
|
||||||
<h2><?= tr('gift_view_by_party') ?></h2>
|
<div class="pf-filter-label">🔍</div>
|
||||||
|
<select id="filterChild" class="pf-filter-select" onchange="applyGiftFilters()">
|
||||||
|
<option value="all">👦 <?= tr('gift_filter_all_children') ?></option>
|
||||||
|
<?php foreach ($children as $c): ?>
|
||||||
|
<option value="<?= htmlspecialchars($c) ?>"><?= htmlspecialchars($c) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="filterAdult" class="pf-filter-select" onchange="applyGiftFilters()">
|
||||||
|
<option value="all">👤 <?= tr('gift_filter_all_adults') ?></option>
|
||||||
|
<?php foreach ($allAdultsList as $a): ?>
|
||||||
|
<option value="<?= htmlspecialchars($a) ?>"><?= htmlspecialchars($a) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="pf-section">
|
||||||
<?php if (empty($gifts)): ?>
|
<?php if (empty($gifts)): ?>
|
||||||
<p class="cl-legend"><?= sprintf(tr('gift_no_gifts'), htmlspecialchars($year)) ?></p>
|
<div style="text-align:center; padding:40px; color:var(--text-muted); font-style:italic; background:white; border-radius:12px; border:1px solid var(--border-light);">
|
||||||
|
<?= sprintf(tr('gift_no_gifts'), $year) ?>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php foreach ($occasionsToShow as $occCode): ?>
|
<?php foreach ($occasionsToShow as $occCode): ?>
|
||||||
<div class="cl-occasion-block">
|
<div class="pf-occasion-wrapper js-occ-wrapper">
|
||||||
<h3 class="cl-occasion-title">
|
<h2 class="cl-occasion-title">
|
||||||
<?php if (!empty($occasionIcons[$occCode])): ?>
|
<?php if (!empty($occasionIcons[$occCode])): ?>
|
||||||
<img class="cl-occasion-icon" src="<?= htmlspecialchars($occasionIcons[$occCode]) ?>" alt="" aria-hidden="true">
|
<img class="cl-occasion-icon" src="<?= htmlspecialchars($occasionIcons[$occCode]) ?>" alt="" aria-hidden="true">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?= htmlspecialchars($allOccasionLabels[$occCode] ?? $occCode) ?>
|
<?= htmlspecialchars($allOccasionLabels[$occCode] ?? $occCode) ?>
|
||||||
</h3>
|
</h2>
|
||||||
|
|
||||||
<div class="cl-occasion-children-tables">
|
<?php foreach ($children as $childName):
|
||||||
<?php foreach ($children as $childName): ?>
|
|
||||||
<?php
|
$childData = $dataByOccasion[$occCode][$childName] ?? ['gifts' => [], 'totals' => []];
|
||||||
$adultsForChild = ($currentView === 'anniversary')
|
$childGifts = $childData['gifts'] ?? [];
|
||||||
? ($adultsByChildForAnniv[$childName] ?? $baseAdults)
|
$childTotals = $childData['totals'] ?? [];
|
||||||
: $baseAdults;
|
$adultsForChild = ($currentView === 'anniversary') ? ($adultsByChildForAnniv[$childName] ?? $baseAdults) : $baseAdults;
|
||||||
|
|
||||||
|
// Sécurisation du JSON pour éviter l'erreur "openGiftModal is not defined"
|
||||||
|
$addBtnData = json_encode([
|
||||||
|
'child' => $childName,
|
||||||
|
'occ' => $occCode,
|
||||||
|
'adults' => array_values($adultsForChild)
|
||||||
|
], JSON_HEX_APOS | JSON_HEX_QUOT);
|
||||||
|
?>
|
||||||
|
<div class="pf-child-section js-child-section" data-child="<?= htmlspecialchars($childName) ?>">
|
||||||
|
<div class="pf-child-header">
|
||||||
|
<h3>👦 <?= htmlspecialchars($childName) ?></h3>
|
||||||
|
<button type="button" class="pf-btn pf-btn-small" onclick='openGiftModal("add", <?= $addBtnData ?>)'>
|
||||||
|
+ <?= tr('gift_add_gift') ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
$lists = [];
|
<?php if (!empty($childTotals)): ?>
|
||||||
$totals = [];
|
<div class="pf-child-totals-bar">
|
||||||
foreach ($adultsForChild as $adultName) {
|
<?php foreach ($childTotals as $adult => $tot): ?>
|
||||||
$lists[$adultName] = $byOccasion[$occCode][$childName][$adultName] ?? [];
|
<span class="pf-summary-pill js-pill-adult" data-adult="<?= htmlspecialchars($adult) ?>">
|
||||||
$totals[$adultName] = array_sum(array_column($lists[$adultName], 'amount'));
|
👤 <?= htmlspecialchars($adult) ?> : <strong><?= number_format($tot, 2, ',', '') ?> €</strong>
|
||||||
}
|
</span>
|
||||||
|
<?php endforeach; ?>
|
||||||
$counts = array_map('count', $lists);
|
</div>
|
||||||
$maxRowsChild = !empty($counts) ? max($counts) : 0;
|
<?php endif; ?>
|
||||||
?>
|
|
||||||
|
|
||||||
<table class="cl-child-table child-<?= strtolower($childName) ?>">
|
|
||||||
<colgroup>
|
|
||||||
<?php foreach ($adultsForChild as $_): ?>
|
|
||||||
<col class="cl-col" />
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</colgroup>
|
|
||||||
|
|
||||||
<caption>
|
<?php if (empty($childGifts)): ?>
|
||||||
<?= htmlspecialchars($childName) ?>
|
<p class="js-empty-state" style="color:var(--text-muted); font-size:0.9rem; font-style:italic; margin:0;"><?= tr('gift_empty_state_no_gifts') ?></p>
|
||||||
<button type="button" class="cl-child-add-btn" title="<?= tr('gift_add_gift') ?>"
|
<?php else: ?>
|
||||||
data-year="<?= $year ?>"
|
<p class="js-empty-state" style="color:var(--text-muted); font-size:0.9rem; font-style:italic; display:none; margin:0;"><?= tr('gift_empty_state_no_filter') ?></p>
|
||||||
data-child="<?= htmlspecialchars($childName) ?>"
|
<div class="pf-gift-feed">
|
||||||
data-occasion="<?= htmlspecialchars($occCode) ?>"
|
<?php foreach ($childGifts as $gift):
|
||||||
data-adults="<?= htmlspecialchars(json_encode(array_values($adultsForChild)), ENT_QUOTES) ?>">
|
$editBtnData = json_encode($gift, JSON_HEX_APOS | JSON_HEX_QUOT);
|
||||||
+
|
?>
|
||||||
</button>
|
<div class="pf-gift-card-compact js-gift-card" data-adult="<?= htmlspecialchars($gift['adult_name']) ?>">
|
||||||
</caption>
|
<div>
|
||||||
|
<h4 class="pf-gift-title">
|
||||||
<thead>
|
<?= htmlspecialchars($gift['gift_description']) ?>
|
||||||
<tr>
|
<?php if(!empty($gift['product_link'])): ?>
|
||||||
<?php foreach ($adultsForChild as $adultName): ?>
|
<a href="<?= htmlspecialchars($gift['product_link']) ?>" target="_blank" class="pf-gift-link" title="Voir l'article">🔗</a>
|
||||||
<th>
|
<?php endif; ?>
|
||||||
<div class="cl-th-inner">
|
</h4>
|
||||||
<span class="cl-th-label"><?= htmlspecialchars($adultName) ?></span>
|
|
||||||
<span class="cl-summary-adult-total"><?= number_format($totals[$adultName], 0, ',', ' ') ?> €</span>
|
<div class="pf-gift-badges-col">
|
||||||
|
<span class="pf-pill-adult">👤 <?= htmlspecialchars($gift['adult_name']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</div>
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
<?php if(!empty($gift['payer_name']) && $gift['payer_name'] !== $gift['adult_name']): ?>
|
||||||
</thead>
|
<div class="pf-gift-payer"><?= sprintf(tr('gift_paid_by'), htmlspecialchars($gift['payer_name'])) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<tbody>
|
<div class="pf-gift-footer">
|
||||||
<?php if ($maxRowsChild === 0): ?>
|
<span class="pf-gift-price"><?= number_format($gift['amount'], 2, ',', ' ') ?> €</span>
|
||||||
<tr>
|
<div class="pf-gift-actions">
|
||||||
<?php foreach ($adultsForChild as $_): ?>
|
<button type="button" class="btn-icon-action edit" aria-label="<?= tr('edit') ?>" onclick='openGiftModal("edit", <?= $editBtnData ?>)'>✏️</button>
|
||||||
<td><span class="cl-empty">—</span></td>
|
<button type="button" class="btn-icon-action delete" aria-label="<?= tr('delete') ?>" onclick="deleteGift(<?= $gift['id'] ?>)">🗑️</button>
|
||||||
<?php endforeach; ?>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
<?php else: ?>
|
</div>
|
||||||
<?php for ($i = 0; $i < $maxRowsChild; $i++): ?>
|
<?php endforeach; ?>
|
||||||
<tr>
|
</div>
|
||||||
<?php foreach ($adultsForChild as $adultName): ?>
|
<?php endif; ?>
|
||||||
<?php $gift = $lists[$adultName][$i] ?? null; ?>
|
</div>
|
||||||
<td>
|
<?php endforeach; ?>
|
||||||
<?php if ($gift): ?>
|
|
||||||
<?php
|
|
||||||
$giftId = (int)$gift['id'];
|
|
||||||
$desc = htmlspecialchars($gift['gift_description']);
|
|
||||||
$amt = (float)$gift['amount'];
|
|
||||||
$plink = trim($gift['product_link'] ?? '');
|
|
||||||
$payer = $gift['payer_name'] ?? $gift['adult_name'];
|
|
||||||
?>
|
|
||||||
<div class="cl-gift-item">
|
|
||||||
<div class="cl-gift-line">
|
|
||||||
<?php if ($plink !== ''): ?>
|
|
||||||
<a href="<?= htmlspecialchars($plink) ?>" target="_blank" rel="noopener noreferrer" class="cl-gift-link"><?= $desc ?></a>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="cl-gift-desc"><?= $desc ?></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="cl-gift-right">
|
|
||||||
<span class="cl-gift-amount">(<?= number_format($amt, 0, ',', ' ') ?> €)</span>
|
|
||||||
<span class="cl-gift-actions">
|
|
||||||
<button type="button" class="btn-icon-action edit cl-gift-edit" aria-label="<?= tr('edit') ?>"
|
|
||||||
data-id="<?= $giftId ?>"
|
|
||||||
data-year="<?= $year ?>"
|
|
||||||
data-child="<?= htmlspecialchars($childName) ?>"
|
|
||||||
data-occasion="<?= htmlspecialchars($occCode) ?>"
|
|
||||||
data-adult="<?= htmlspecialchars($gift['adult_name']) ?>"
|
|
||||||
data-payer="<?= htmlspecialchars($payer) ?>"
|
|
||||||
data-desc="<?= htmlspecialchars($gift['gift_description']) ?>"
|
|
||||||
data-amount="<?= htmlspecialchars($gift['amount']) ?>"
|
|
||||||
data-link="<?= htmlspecialchars($gift['product_link'] ?? '') ?>">
|
|
||||||
✏️
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn-icon-action delete cl-gift-delete" aria-label="<?= tr('delete') ?>" data-id="<?= $giftId ?>">
|
|
||||||
🗑️
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (!empty($payer) && $payer !== $gift['adult_name']): ?>
|
|
||||||
<small style="color:#b91c1c; font-style:italic; display:block; font-size:0.75em;">(<?= sprintf(tr('gift_paid_by'), htmlspecialchars($payer)) ?>)</small>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="cl-empty">—</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endfor; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="pf-section pf-section--panel">
|
<section class="pf-section pf-section--panel" style="background:#f8fafc; border:1px solid var(--border-light); margin-top:40px;">
|
||||||
<h2><?= tr('gift_summary_title') ?></h2>
|
<h2 style="margin-top:0; color:var(--text-main); font-size:1.3rem;">⚖️ <?= tr('gift_liquidations') ?? 'Bilan & Remboursements' ?></h2>
|
||||||
<?php
|
|
||||||
$stmtSum = $pdo->prepare("SELECT adult_name, child_name, occasion, SUM(amount) AS total FROM {$tableGifts} WHERE year = ? AND occasion IN ($inMarks) GROUP BY adult_name, child_name, occasion ORDER BY adult_name, child_name, occasion");
|
|
||||||
$stmtSum->execute($params);
|
|
||||||
$sums = $stmtSum->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
?>
|
|
||||||
<div class="cl-budget-wrapper">
|
|
||||||
<table class="pf-table pf-table--compact">
|
|
||||||
<thead>
|
|
||||||
<tr><th><?= tr('gift_col_adult') ?></th><th><?= tr('gift_col_child') ?></th><th><?= tr('gift_col_party') ?></th><th>Total</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($sums as $row): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= htmlspecialchars($row['adult_name']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($row['child_name']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($allOccasionLabels[$row['occasion']] ?? $row['occasion']) ?></td>
|
|
||||||
<td><?= number_format($row['total'], 0, ',', ' ') ?> €</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="pf-section pf-section--panel">
|
|
||||||
<h2>Tricount</h2>
|
|
||||||
<div class="cl-budget-wrapper">
|
|
||||||
<table class="pf-table pf-table--compact cl-debt-matrix">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="cl-matrix-corner"><span><?= tr('gift_debtor') ?> ↓</span><span><?= tr('gift_creditor') ?> →</span></th>
|
|
||||||
<?php foreach ($people as $p): ?><th><?= htmlspecialchars($p) ?></th><?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($people as $debtor): ?>
|
|
||||||
<tr>
|
|
||||||
<th><?= htmlspecialchars($debtor) ?></th>
|
|
||||||
<?php foreach ($people as $creditor): ?>
|
|
||||||
<?php
|
|
||||||
$val = $matrix[$debtor][$creditor] ?? 0;
|
|
||||||
$isDiag = ($debtor === $creditor);
|
|
||||||
$cls = $isDiag ? 'cl-mtx-diag' : ($val > 0 ? 'cl-mtx-owe' : 'cl-mtx-empty');
|
|
||||||
$display = $isDiag || $val == 0 ? '—' : number_format($val, 0, ',', ' ') . ' €';
|
|
||||||
?>
|
|
||||||
<td class="<?= $cls ?>"><?= $display ?></td>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 style="margin-top:16px; font-size:1.1rem; color:#374151;"><?= tr('gift_liquidations') ?></h3>
|
|
||||||
<?php if (empty($settlements)): ?>
|
<?php if (empty($settlements)): ?>
|
||||||
<p class="cl-legend"><?= tr('gift_no_debt') ?></p>
|
<p style="color:var(--success); font-weight:700; margin-bottom:0;">✅ <?= tr('gift_no_debt') ?></p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<ul class="hol-list">
|
<ul class="pf-tricount-list">
|
||||||
<?php foreach ($settlements as $s): ?>
|
<?php foreach ($settlements as $s): ?>
|
||||||
<li><strong><?= htmlspecialchars($s['from']) ?></strong> <?= tr('gift_owes') ?> <strong><?= number_format($s['amount'], 2, ',', ' ') ?> €</strong> <?= tr('gift_to') ?> <?= htmlspecialchars($s['to']) ?></li>
|
<li class="pf-tricount-item">
|
||||||
|
<span><strong><?= htmlspecialchars($s['from']) ?></strong> <?= tr('gift_owes') ?> à <?= htmlspecialchars($s['to']) ?></span>
|
||||||
|
<strong style="color:var(--danger);"><?= number_format($s['amount'], 2, ',', ' ') ?> €</strong>
|
||||||
|
</li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="pf-section pf-section--panel">
|
<details style="margin-top:20px; background:white; padding:12px; border-radius:8px; border:1px solid #cbd5e1;">
|
||||||
<h2><?= tr('gift_detailed_list') ?></h2>
|
<summary style="cursor:pointer; font-weight:600; color:var(--text-muted); outline:none;"><?= tr('gift_view_matrix') ?></summary>
|
||||||
<div class="cl-detail-wrapper">
|
<div style="overflow-x:auto; margin-top:15px;">
|
||||||
<table class="pf-table pf-table--compact">
|
<table class="pf-table pf-table--compact cl-debt-matrix">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th><?= tr('gift_col_adult') ?></th><th><?= tr('gift_col_child') ?></th><th><?= tr('gift_col_party') ?></th><th><?= tr('gift_col_gift') ?></th><th>€</th><th><?= tr('gift_col_link') ?></th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($gifts as $g): ?>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= htmlspecialchars($g['adult_name']) ?></td>
|
<th style="position:sticky; left:0; background:#f8fafc; z-index:2; border-right:2px solid #e2e8f0;"><?= tr('gift_debtor') ?> \ <?= tr('gift_creditor') ?></th>
|
||||||
<td><?= htmlspecialchars($g['child_name']) ?></td>
|
<?php foreach ($people as $p): ?><th><?= htmlspecialchars($p) ?></th><?php endforeach; ?>
|
||||||
<td><?= htmlspecialchars($allOccasionLabels[$g['occasion']] ?? $g['occasion']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($g['gift_description']) ?></td>
|
|
||||||
<td><?= number_format($g['amount'], 0, ',', ' ') ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if (!empty($g['product_link'])): ?>
|
|
||||||
<a href="<?= htmlspecialchars($g['product_link']) ?>" target="_blank">🔗</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
<?php foreach ($people as $debtor): ?>
|
||||||
</div>
|
<tr>
|
||||||
|
<th style="position:sticky; left:0; background:white; z-index:2; border-right:2px solid #e2e8f0;"><?= htmlspecialchars($debtor) ?></th>
|
||||||
|
<?php foreach ($people as $creditor):
|
||||||
|
$val = $matrix[$debtor][$creditor] ?? 0;
|
||||||
|
$isDiag = ($debtor === $creditor);
|
||||||
|
$display = $isDiag || $val == 0 ? '—' : number_format($val, 2, ',', ' ') . ' €';
|
||||||
|
?>
|
||||||
|
<td style="<?= $val > 0 ? 'color:var(--danger); font-weight:700; background:#fef2f2;' : 'color:var(--text-muted);' ?>"><?= $display ?></td>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<div id="pf-gift-modal" class="pf-modal">
|
||||||
// Pont de traduction JS
|
<div class="pf-modal-content" style="max-width: 500px; width: 95%;">
|
||||||
window.I18N = {
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
|
||||||
...window.I18N,
|
<h3 id="pf-modal-title" class="pf-modal-title" style="margin:0; border:none; padding:0;"><?= tr('gift_col_gift') ?></h3>
|
||||||
'gift_modal_title_add': "<?= tr('gift_modal_title_add') ?>",
|
<button type="button" onclick="closeGiftModal()" style="background:none; border:none; font-size:1.8rem; cursor:pointer; color:var(--text-muted); line-height:1;">×</button>
|
||||||
'gift_modal_title_edit': "<?= tr('gift_modal_title_edit') ?>",
|
</div>
|
||||||
'gift_confirm_delete': "<?= tr('gift_confirm_delete') ?>"
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
const modal = document.getElementById('cl-gift-modal');
|
|
||||||
const backdrop = modal ? modal.querySelector('.cl-modal-backdrop') : null;
|
|
||||||
const cancelBtn = modal ? modal.querySelector('.clm-cancel') : null;
|
|
||||||
|
|
||||||
function toggleModal(show) {
|
|
||||||
if (!modal) return;
|
|
||||||
modal.classList.toggle('cl-open', show);
|
|
||||||
if(show) document.body.classList.add('no-scroll');
|
|
||||||
else document.body.classList.remove('no-scroll');
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateSelects(adults) {
|
|
||||||
const selects = [document.getElementById('clm-adult'), document.getElementById('clm-payer')];
|
|
||||||
selects.forEach(sel => {
|
|
||||||
if (!sel) return;
|
|
||||||
sel.innerHTML = '';
|
|
||||||
adults.forEach(name => {
|
|
||||||
const opt = document.createElement('option');
|
|
||||||
opt.value = name;
|
|
||||||
opt.textContent = name;
|
|
||||||
sel.appendChild(opt);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BOUTON AJOUT (+) ---
|
|
||||||
document.querySelectorAll('.cl-child-add-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const d = btn.dataset;
|
|
||||||
let adults = [];
|
|
||||||
try { adults = JSON.parse(d.adults || '[]'); } catch(e) {}
|
|
||||||
|
|
||||||
populateSelects(adults);
|
|
||||||
|
|
||||||
document.getElementById('clm-action').value = 'create';
|
|
||||||
document.getElementById('clm-id').value = '';
|
|
||||||
document.getElementById('clm-year').value = d.year;
|
|
||||||
document.getElementById('clm-child').value = d.child;
|
|
||||||
document.getElementById('clm-occasion').value = d.occasion;
|
|
||||||
|
|
||||||
document.getElementById('clm-gift').value = '';
|
|
||||||
document.getElementById('clm-amount').value = '';
|
|
||||||
document.getElementById('clm-link').value = '';
|
|
||||||
|
|
||||||
document.getElementById('cl-modal-title').textContent = tr('gift_modal_title_add').replace('%s', d.child);
|
|
||||||
toggleModal(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- BOUTON ÉDITION (Crayon) ---
|
|
||||||
document.body.addEventListener('click', (e) => {
|
|
||||||
const btn = e.target.closest('.cl-gift-edit');
|
|
||||||
if (!btn) return;
|
|
||||||
|
|
||||||
const d = btn.dataset;
|
|
||||||
const adultSelect = document.getElementById('clm-adult');
|
|
||||||
const payerSelect = document.getElementById('clm-payer');
|
|
||||||
|
|
||||||
[adultSelect, payerSelect].forEach(sel => {
|
<form method="post" action="/modules/gift-list/save-gift.php" id="pf-gift-form">
|
||||||
const val = (sel === adultSelect) ? d.adult : (d.payer || d.adult);
|
<input type="hidden" name="year" id="m_year" value="<?= $year ?>">
|
||||||
if (!Array.from(sel.options).some(o => o.value === val)) {
|
<input type="hidden" name="child_name" id="m_child">
|
||||||
const opt = document.createElement('option');
|
<input type="hidden" name="occasion" id="m_occ">
|
||||||
opt.value = val;
|
<input type="hidden" name="action" id="m_action">
|
||||||
opt.textContent = val;
|
<input type="hidden" name="gift_id" id="m_id">
|
||||||
sel.appendChild(opt);
|
|
||||||
}
|
|
||||||
sel.value = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('clm-action').value = 'update';
|
<div class="pf-form-group">
|
||||||
document.getElementById('clm-id').value = d.id;
|
|
||||||
document.getElementById('clm-year').value = d.year;
|
|
||||||
document.getElementById('clm-child').value = d.child;
|
|
||||||
document.getElementById('clm-occasion').value = d.occasion;
|
|
||||||
|
|
||||||
document.getElementById('clm-gift').value = d.desc;
|
|
||||||
document.getElementById('clm-amount').value = d.amount;
|
|
||||||
document.getElementById('clm-link').value = d.link;
|
|
||||||
|
|
||||||
document.getElementById('cl-modal-title').textContent = tr('gift_modal_title_edit');
|
|
||||||
toggleModal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- SUPPRESSION ---
|
|
||||||
document.body.addEventListener('click', (e) => {
|
|
||||||
const btn = e.target.closest('.cl-gift-delete');
|
|
||||||
if (!btn) return;
|
|
||||||
|
|
||||||
if (confirm(tr('gift_confirm_delete'))) {
|
|
||||||
const form = document.getElementById('cl-delete-form');
|
|
||||||
document.getElementById('cld-id').value = btn.dataset.id;
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fermeture
|
|
||||||
if(cancelBtn) cancelBtn.addEventListener('click', () => toggleModal(false));
|
|
||||||
if(backdrop) backdrop.addEventListener('click', () => toggleModal(false));
|
|
||||||
document.addEventListener('keydown', (e) => { if(e.key === 'Escape') toggleModal(false); });
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="cl-gift-modal" class="cl-modal" aria-hidden="true">
|
|
||||||
<div class="cl-modal-backdrop"></div>
|
|
||||||
<div class="cl-modal-dialog pf-modal-content" role="dialog" aria-modal="true" aria-labelledby="cl-modal-title">
|
|
||||||
<form method="post" action="/modules/gift-list/save-gift.php" class="cl-modal-form">
|
|
||||||
<h3 id="cl-modal-title" style="margin-top:0;"><?= tr('gift_col_gift') ?></h3>
|
|
||||||
|
|
||||||
<input type="hidden" name="year" id="clm-year">
|
|
||||||
<input type="hidden" name="child_name" id="clm-child">
|
|
||||||
<input type="hidden" name="occasion" id="clm-occasion">
|
|
||||||
<input type="hidden" name="action" id="clm-action">
|
|
||||||
<input type="hidden" name="gift_id" id="clm-id">
|
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom:15px;">
|
|
||||||
<label class="pf-label"><?= tr('gift_col_adult') ?></label>
|
<label class="pf-label"><?= tr('gift_col_adult') ?></label>
|
||||||
<select name="adult_name" id="clm-adult" class="pf-input" required></select>
|
<select name="adult_name" id="m_adult" class="pf-input" required></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom:15px;">
|
<div class="pf-form-group">
|
||||||
<label class="pf-label"><?= tr('gift_modal_payer') ?></label>
|
<label class="pf-label"><?= tr('gift_modal_payer') ?></label>
|
||||||
<select name="payer_name" id="clm-payer" class="pf-input" required></select>
|
<select name="payer_name" id="m_payer" class="pf-input" required></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom:15px;">
|
<div class="pf-form-group">
|
||||||
<label class="pf-label"><?= tr('gift_modal_gift_name') ?></label>
|
<label class="pf-label"><?= tr('gift_modal_gift_name') ?></label>
|
||||||
<input type="text" name="gift_description" id="clm-gift" class="pf-input" placeholder="<?= tr('gift_modal_ph_name') ?>" required>
|
<input type="text" name="gift_description" id="m_gift" class="pf-input" placeholder="<?= tr('gift_modal_ph_name') ?>" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom:15px;">
|
<div class="pf-form-group">
|
||||||
<label class="pf-label"><?= tr('gift_modal_price') ?></label>
|
<label class="pf-label"><?= tr('gift_modal_price') ?></label>
|
||||||
<input type="number" name="amount" id="clm-amount" class="pf-input" placeholder="49.99" step="0.01" min="0">
|
<input type="number" name="amount" id="m_amount" class="pf-input" placeholder="0.00" step="0.01" min="0">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom:25px;">
|
<div class="pf-form-group" style="margin-bottom:25px;">
|
||||||
<label class="pf-label"><?= tr('gift_modal_link') ?></label>
|
<label class="pf-label"><?= tr('gift_modal_link') ?></label>
|
||||||
<input type="url" name="product_link" id="clm-link" class="pf-input" placeholder="https://...">
|
<input type="url" name="product_link" id="m_link" class="pf-input" placeholder="https://...">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer" style="padding-top:15px; border-top:1px solid #e2e8f0; display:flex; justify-content:flex-end; gap:10px;">
|
<div class="modal-footer" style="padding-top:15px; border-top:1px solid #e2e8f0; display:flex; justify-content:flex-end; gap:10px;">
|
||||||
<button type="button" class="clm-cancel pf-btn btn-secondary"><?= tr('btn_cancel') ?></button>
|
<button type="button" onclick="closeGiftModal()" class="pf-btn btn-secondary"><?= tr('btn_cancel') ?></button>
|
||||||
<button type="submit" class="clm-ok pf-btn"><?= tr('btn_save') ?></button>
|
<button type="submit" class="pf-btn"><?= tr('btn_save') ?></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="cl-delete-form" method="post" action="/modules/gift-list/save-gift.php" style="display:none">
|
<script>
|
||||||
<input type="hidden" name="year" value="<?= $year ?>">
|
// --- SYNCHRONISATION AUTO : ADULTE -> PAYEUR ---
|
||||||
<input type="hidden" name="action" value="delete">
|
document.getElementById('m_adult').addEventListener('change', function() {
|
||||||
<input type="hidden" name="gift_id" id="cld-id" value="">
|
const payerSelect = document.getElementById('m_payer');
|
||||||
</form>
|
// Si la valeur choisie pour l'adulte existe dans la liste des payeurs, on l'applique
|
||||||
|
if (Array.from(payerSelect.options).some(o => o.value === this.value)) {
|
||||||
|
payerSelect.value = this.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- LOGIQUE DES FILTRES ---
|
||||||
|
function applyGiftFilters() {
|
||||||
|
const fChild = document.getElementById('filterChild').value;
|
||||||
|
const fAdult = document.getElementById('filterAdult').value;
|
||||||
|
|
||||||
|
document.querySelectorAll('.js-occ-wrapper').forEach(occWrapper => {
|
||||||
|
let hasVisibleChildrenInOccasion = false;
|
||||||
|
|
||||||
|
occWrapper.querySelectorAll('.js-child-section').forEach(childSec => {
|
||||||
|
const cName = childSec.dataset.child;
|
||||||
|
const matchChild = (fChild === 'all' || cName === fChild);
|
||||||
|
let visibleCount = 0;
|
||||||
|
|
||||||
|
childSec.querySelectorAll('.js-gift-card').forEach(card => {
|
||||||
|
const matchAdult = (fAdult === 'all' || card.dataset.adult === fAdult);
|
||||||
|
if (matchChild && matchAdult) {
|
||||||
|
card.style.display = 'flex';
|
||||||
|
visibleCount++;
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
childSec.querySelectorAll('.js-pill-adult').forEach(pill => {
|
||||||
|
pill.style.opacity = (fAdult === 'all' || pill.dataset.adult === fAdult) ? '1' : '0.3';
|
||||||
|
});
|
||||||
|
|
||||||
|
const emptyState = childSec.querySelector('.js-empty-state');
|
||||||
|
if (matchChild) {
|
||||||
|
childSec.style.display = 'block';
|
||||||
|
hasVisibleChildrenInOccasion = true;
|
||||||
|
if (emptyState) emptyState.style.display = (visibleCount === 0) ? 'block' : 'none';
|
||||||
|
} else {
|
||||||
|
childSec.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Masquer complètement la fête (ex: Tió) s'il n'y a plus aucun enfant visible dedans
|
||||||
|
occWrapper.style.display = hasVisibleChildrenInOccasion ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- LOGIQUE DE LA MODALE ---
|
||||||
|
const modal = document.getElementById('pf-gift-modal');
|
||||||
|
const adultSelect = document.getElementById('m_adult');
|
||||||
|
const payerSelect = document.getElementById('m_payer');
|
||||||
|
|
||||||
|
function populateSelects(adults) {
|
||||||
|
adultSelect.innerHTML = '';
|
||||||
|
payerSelect.innerHTML = '';
|
||||||
|
adults.forEach(name => {
|
||||||
|
adultSelect.appendChild(new Option(name, name));
|
||||||
|
payerSelect.appendChild(new Option(name, name));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGiftModal(mode, data) {
|
||||||
|
if (mode === 'add') {
|
||||||
|
document.getElementById('m_action').value = 'create';
|
||||||
|
document.getElementById('m_id').value = '';
|
||||||
|
document.getElementById('m_child').value = data.child;
|
||||||
|
document.getElementById('m_occ').value = data.occ; // L'occasion est passée par le bouton !
|
||||||
|
document.getElementById('m_gift').value = '';
|
||||||
|
document.getElementById('m_amount').value = '';
|
||||||
|
document.getElementById('m_link').value = '';
|
||||||
|
|
||||||
|
populateSelects(data.adults);
|
||||||
|
document.getElementById('pf-modal-title').textContent = (window.I18N && window.I18N['gift_modal_title_add'] ? window.I18N['gift_modal_title_add'] : 'Ajouter pour %s').replace('%s', data.child);
|
||||||
|
}
|
||||||
|
else if (mode === 'edit') {
|
||||||
|
document.getElementById('m_action').value = 'update';
|
||||||
|
document.getElementById('m_id').value = data.id;
|
||||||
|
document.getElementById('m_child').value = data.child_name;
|
||||||
|
document.getElementById('m_occ').value = data.occasion;
|
||||||
|
document.getElementById('m_gift').value = data.gift_description;
|
||||||
|
document.getElementById('m_amount').value = data.amount;
|
||||||
|
document.getElementById('m_link').value = data.product_link;
|
||||||
|
|
||||||
|
const payerVal = data.payer_name || data.adult_name;
|
||||||
|
if (!Array.from(adultSelect.options).some(o => o.value === data.adult_name)) {
|
||||||
|
adultSelect.appendChild(new Option(data.adult_name, data.adult_name));
|
||||||
|
}
|
||||||
|
if (!Array.from(payerSelect.options).some(o => o.value === payerVal)) {
|
||||||
|
payerSelect.appendChild(new Option(payerVal, payerVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
adultSelect.value = data.adult_name;
|
||||||
|
payerSelect.value = payerVal;
|
||||||
|
|
||||||
|
document.getElementById('pf-modal-title').textContent = window.I18N && window.I18N['gift_modal_title_edit'] ? window.I18N['gift_modal_title_edit'] : 'Modifier le cadeau';
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.classList.add('open');
|
||||||
|
document.body.classList.add('no-scroll');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeGiftModal() {
|
||||||
|
modal.classList.remove('open');
|
||||||
|
document.body.classList.remove('no-scroll');
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- SOUMISSION AJAX ---
|
||||||
|
document.getElementById('pf-gift-form').addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const btn = e.target.querySelector('button[type="submit"]');
|
||||||
|
const oldText = btn.innerText;
|
||||||
|
btn.innerText = '...'; btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch(e.target.action, { method: 'POST', body: new FormData(e.target) });
|
||||||
|
window.location.reload();
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
btn.innerText = oldText; btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- SUPPRESSION AJAX ---
|
||||||
|
async function deleteGift(id) {
|
||||||
|
const msg = window.I18N && window.I18N['gift_confirm_delete'] ? window.I18N['gift_confirm_delete'] : 'Supprimer ce cadeau ?';
|
||||||
|
if (!confirm(msg)) return;
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('action', 'delete');
|
||||||
|
fd.append('gift_id', id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fetch('/modules/gift-list/save-gift.php', { method: 'POST', body: fd });
|
||||||
|
window.location.reload();
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<?php require __DIR__ . '/footer.php'; ?>
|
<?php require __DIR__ . '/footer.php'; ?>
|
||||||
@@ -517,7 +517,7 @@ return [
|
|||||||
'gift_col_party' => 'Festa',
|
'gift_col_party' => 'Festa',
|
||||||
'gift_debtor' => 'Deutor',
|
'gift_debtor' => 'Deutor',
|
||||||
'gift_creditor' => 'Creditor',
|
'gift_creditor' => 'Creditor',
|
||||||
'gift_liquidations' => 'Liquidacions',
|
'gift_liquidations' => 'Tricount',
|
||||||
'gift_no_debt' => 'Cap deute pendent.',
|
'gift_no_debt' => 'Cap deute pendent.',
|
||||||
'gift_owes' => 'ha de pagar',
|
'gift_owes' => 'ha de pagar',
|
||||||
'gift_to' => 'a',
|
'gift_to' => 'a',
|
||||||
@@ -532,4 +532,9 @@ return [
|
|||||||
'gift_modal_price' => 'Preu (€)',
|
'gift_modal_price' => 'Preu (€)',
|
||||||
'gift_modal_link' => 'Enllaç (opcional)',
|
'gift_modal_link' => 'Enllaç (opcional)',
|
||||||
'gift_confirm_delete' => 'Vols eliminar aquest regal?',
|
'gift_confirm_delete' => 'Vols eliminar aquest regal?',
|
||||||
|
'gift_filter_all_children' => 'Tots els nens',
|
||||||
|
'gift_filter_all_adults' => 'Tots els adults',
|
||||||
|
'gift_empty_state_no_gifts' => 'Cap regal de moment.',
|
||||||
|
'gift_empty_state_no_filter' => 'Cap regal correspon al filtre.',
|
||||||
|
'gift_view_matrix' => 'Veure la matriu detallada',
|
||||||
];
|
];
|
||||||
@@ -501,7 +501,7 @@ return [
|
|||||||
'gift_page_title' => 'PachaFamily - Liste de cadeaux',
|
'gift_page_title' => 'PachaFamily - Liste de cadeaux',
|
||||||
'gift_occ_tio' => 'Tió',
|
'gift_occ_tio' => 'Tió',
|
||||||
'gift_occ_noel' => 'Noël',
|
'gift_occ_noel' => 'Noël',
|
||||||
'gift_occ_rois' => 'Rois',
|
'gift_occ_rois' => 'Rois mages',
|
||||||
'gift_occ_anniv' => 'Anniversaire',
|
'gift_occ_anniv' => 'Anniversaire',
|
||||||
'gift_occ_sant' => 'Saint',
|
'gift_occ_sant' => 'Saint',
|
||||||
'gift_main_title' => 'Liste de cadeaux %s',
|
'gift_main_title' => 'Liste de cadeaux %s',
|
||||||
@@ -518,7 +518,7 @@ return [
|
|||||||
'gift_col_party' => 'Fête',
|
'gift_col_party' => 'Fête',
|
||||||
'gift_debtor' => 'Débiteur',
|
'gift_debtor' => 'Débiteur',
|
||||||
'gift_creditor' => 'Créancier',
|
'gift_creditor' => 'Créancier',
|
||||||
'gift_liquidations' => 'Liquidations',
|
'gift_liquidations' => 'Tricount',
|
||||||
'gift_no_debt' => 'Aucune dette en cours.',
|
'gift_no_debt' => 'Aucune dette en cours.',
|
||||||
'gift_owes' => 'doit',
|
'gift_owes' => 'doit',
|
||||||
'gift_to' => 'à',
|
'gift_to' => 'à',
|
||||||
@@ -533,4 +533,9 @@ return [
|
|||||||
'gift_modal_price' => 'Prix (€)',
|
'gift_modal_price' => 'Prix (€)',
|
||||||
'gift_modal_link' => 'Lien (optionnel)',
|
'gift_modal_link' => 'Lien (optionnel)',
|
||||||
'gift_confirm_delete' => 'Voulez-vous vraiment supprimer ce cadeau ?',
|
'gift_confirm_delete' => 'Voulez-vous vraiment supprimer ce cadeau ?',
|
||||||
|
'gift_filter_all_children' => 'Tous les enfants',
|
||||||
|
'gift_filter_all_adults' => 'Tous les adultes',
|
||||||
|
'gift_empty_state_no_gifts' => 'Aucun cadeau pour le moment.',
|
||||||
|
'gift_empty_state_no_filter' => 'Aucun cadeau ne correspond au filtre.',
|
||||||
|
'gift_view_matrix' => 'Voir la matrice détaillée',
|
||||||
];
|
];
|
||||||
+198
-556
@@ -1,50 +1,21 @@
|
|||||||
/* modules/gift-list/gift-list.css */
|
/* modules/gift-list/gift-list.css */
|
||||||
|
|
||||||
/* --- 1. VARIABLES & BASE --- */
|
|
||||||
:root {
|
|
||||||
--primary: #2563eb;
|
|
||||||
--bg-page: #f8fafc;
|
|
||||||
--text-main: #1e293b;
|
|
||||||
--text-muted: #64748b;
|
|
||||||
--border-light: #e2e8f0;
|
|
||||||
|
|
||||||
--radius-card: 12px;
|
|
||||||
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pf-gift-list {
|
|
||||||
background-color: var(--bg-page);
|
|
||||||
font-family:
|
|
||||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
|
|
||||||
sans-serif;
|
|
||||||
color: var(--text-main);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pf-gift-list h1 {
|
.pf-gift-list h1 {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
color: #0f172a;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
color: var(--text-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-legend {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-style: italic;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- 2. HEADER & SWITCH --- */
|
|
||||||
.cl-titlebar {
|
.cl-titlebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 1rem;
|
gap: 15px;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 1rem;
|
|
||||||
border-bottom: 1px solid var(--border-light);
|
border-bottom: 1px solid var(--border-light);
|
||||||
|
padding-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-view-switch {
|
.cl-view-switch {
|
||||||
@@ -52,7 +23,8 @@
|
|||||||
background: white;
|
background: white;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #cbd5e1;
|
border: 1px solid var(--border-light);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-view-btn {
|
.cl-view-btn {
|
||||||
@@ -62,6 +34,7 @@
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-view-btn.is-active {
|
.cl-view-btn.is-active {
|
||||||
@@ -69,574 +42,243 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 3. BLOCS OCCASIONS & GRILLE --- */
|
/* === FILTRES STICKY === */
|
||||||
.cl-occasion-block {
|
.pf-filter-bar {
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-occasion-title {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #334155;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
text-transform: uppercase;
|
align-items: center;
|
||||||
letter-spacing: 0.05em;
|
margin-bottom: 24px;
|
||||||
border-bottom: 2px solid #e2e8f0;
|
position: sticky;
|
||||||
padding-bottom: 8px;
|
top: 64px;
|
||||||
|
z-index: 50;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-occasion-icon {
|
.pf-filter-label {
|
||||||
width: 24px;
|
font-size: 0.85rem;
|
||||||
height: 24px;
|
font-weight: 600;
|
||||||
object-fit: contain;
|
color: var(--text-muted);
|
||||||
}
|
|
||||||
|
|
||||||
/* --- GRILLE INTELLIGENTE --- */
|
|
||||||
.cl-occasion-children-tables {
|
|
||||||
display: grid;
|
|
||||||
gap: 20px;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VUE STANDARD (Nadal) : 3 par ligne */
|
|
||||||
.cl-view-nadal .cl-occasion-children-tables {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* VUE ANNIVERSARY : Layout spécifique demandé */
|
|
||||||
.cl-view-anniversary .cl-occasion-children-tables {
|
|
||||||
grid-template-columns: repeat(6, 1fr); /* Grille de 6 colonnes */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pol et Pep : Prennent 3 colonnes sur 6 (donc 50% de largeur => 2 par ligne) */
|
|
||||||
.cl-view-anniversary .child-pol,
|
|
||||||
.cl-view-anniversary .child-pep {
|
|
||||||
grid-column: span 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Les autres : Prennent 2 colonnes sur 6 (donc 33% de largeur => 3 par ligne) */
|
|
||||||
.cl-view-anniversary .child-elna,
|
|
||||||
.cl-view-anniversary .child-bru,
|
|
||||||
.cl-view-anniversary .child-guim {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- 4. TABLEAU PAR ENFANT (Card Unifiée) --- */
|
|
||||||
.cl-child-table {
|
|
||||||
background: white;
|
|
||||||
box-shadow: var(--shadow-card);
|
|
||||||
border-radius: 0 0 var(--radius-card) var(--radius-card);
|
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
border-top: none;
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
table-layout: fixed;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CAPTION (Nom de l'enfant - Haut de la carte) */
|
|
||||||
.cl-child-table caption {
|
|
||||||
caption-side: top;
|
|
||||||
display: table-caption;
|
|
||||||
padding: 10px 16px;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
border-radius: var(--radius-card) var(--radius-card) 0 0;
|
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
border-bottom: 2px solid;
|
|
||||||
margin-bottom: 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bouton Ajout (+) Rotatif */
|
|
||||||
.cl-child-add-btn {
|
|
||||||
position: absolute;
|
|
||||||
right: 12px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background: rgba(255, 255, 255, 0.6);
|
|
||||||
color: inherit;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 1;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 6px;
|
||||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
||||||
z-index: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-child-add-btn:hover {
|
.pf-filter-select {
|
||||||
background: white;
|
padding: 6px 12px;
|
||||||
transform: translateY(-50%) rotate(90deg) scale(1.1);
|
border-radius: 20px; /* Forme de pillule */
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
|
border: 1px solid var(--border-light);
|
||||||
border-color: currentColor;
|
background: rgba(255, 255, 255, 0.95);
|
||||||
}
|
backdrop-filter: blur(8px);
|
||||||
|
|
||||||
/* --- 5. COULEURS PAR ENFANT --- */
|
|
||||||
/* POL (Bleu) */
|
|
||||||
.child-pol {
|
|
||||||
border-color: #bcd3ff;
|
|
||||||
}
|
|
||||||
.child-pol caption {
|
|
||||||
background: #eaf2ff;
|
|
||||||
color: #1e3a8a;
|
|
||||||
border-color: #bcd3ff;
|
|
||||||
border-bottom-color: #93c5fd;
|
|
||||||
}
|
|
||||||
.child-pol thead th {
|
|
||||||
background: #f5f9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PEP (Vert) */
|
|
||||||
.child-pep {
|
|
||||||
border-color: #b9e3b9;
|
|
||||||
}
|
|
||||||
.child-pep caption {
|
|
||||||
background: #eaf7ea;
|
|
||||||
color: #14532d;
|
|
||||||
border-color: #b9e3b9;
|
|
||||||
border-bottom-color: #86efac;
|
|
||||||
}
|
|
||||||
.child-pep thead th {
|
|
||||||
background: #f6fdf6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ELNA (Rose) */
|
|
||||||
.child-elna {
|
|
||||||
border-color: #f3bfd7;
|
|
||||||
}
|
|
||||||
.child-elna caption {
|
|
||||||
background: #fdeaf3;
|
|
||||||
color: #831843;
|
|
||||||
border-color: #f3bfd7;
|
|
||||||
border-bottom-color: #f9a8d4;
|
|
||||||
}
|
|
||||||
.child-elna thead th {
|
|
||||||
background: #fff5f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BRU (Orange) */
|
|
||||||
.child-bru {
|
|
||||||
border-color: #ffd0a8;
|
|
||||||
}
|
|
||||||
.child-bru caption {
|
|
||||||
background: #fff3e6;
|
|
||||||
color: #7c2d12;
|
|
||||||
border-color: #ffd0a8;
|
|
||||||
border-bottom-color: #fdba74;
|
|
||||||
}
|
|
||||||
.child-bru thead th {
|
|
||||||
background: #fffaf5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GUIM (Violet) */
|
|
||||||
.child-guim {
|
|
||||||
border-color: #d3c6ff;
|
|
||||||
}
|
|
||||||
.child-guim caption {
|
|
||||||
background: #f0eaff;
|
|
||||||
color: #4c1d95;
|
|
||||||
border-color: #d3c6ff;
|
|
||||||
border-bottom-color: #c4b5fd;
|
|
||||||
}
|
|
||||||
.child-guim thead th {
|
|
||||||
background: #fbf9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- 6. CONTENU DU TABLEAU --- */
|
|
||||||
.cl-child-table thead th {
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
border-bottom: 1px solid var(--border-light);
|
|
||||||
border-right: 1px solid var(--border-light);
|
|
||||||
white-space: normal;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.cl-child-table thead th:last-child {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-th-inner {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
.cl-summary-adult-total {
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: var(--primary);
|
|
||||||
background: rgba(37, 99, 235, 0.1);
|
|
||||||
padding: 1px 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-child-table tbody td {
|
|
||||||
padding: 8px;
|
|
||||||
vertical-align: top;
|
|
||||||
border-bottom: 1px solid #f1f5f9;
|
|
||||||
border-right: 1px solid #f1f5f9;
|
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
font-weight: 600;
|
||||||
.cl-child-table tbody td:last-child {
|
color: var(--text-main);
|
||||||
border-right: none;
|
outline: none;
|
||||||
}
|
font-family: inherit;
|
||||||
.cl-child-table tr:last-child td {
|
cursor: pointer;
|
||||||
border-bottom: none;
|
box-shadow: var(--shadow-sm);
|
||||||
|
width: auto; /* Empêche de prendre toute la largeur */
|
||||||
|
appearance: none; /* Nettoie la flèche système... */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
/* ...pour mettre une flèche personnalisée plus discrète */
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2364748b'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
background-size: 12px;
|
||||||
|
padding-right: 28px; /* Place pour la flèche */
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-empty {
|
.pf-filter-select:focus {
|
||||||
color: #e2e8f0;
|
border-color: var(--primary);
|
||||||
text-align: center;
|
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
|
||||||
display: block;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 7. CADEAUX & INTERACTION (Overlay) --- */
|
/* === SECTIONS ENFANTS === */
|
||||||
.cl-gift-item {
|
.pf-child-section {
|
||||||
display: flex;
|
margin-bottom: 30px;
|
||||||
flex-direction: column;
|
background: white;
|
||||||
gap: 2px;
|
padding: 20px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
.pf-child-header {
|
||||||
.cl-gift-line {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
|
||||||
gap: 4px;
|
|
||||||
position: relative;
|
|
||||||
min-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-gift-desc,
|
|
||||||
.cl-gift-link {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #0f172a;
|
|
||||||
line-height: 1.2;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
hyphens: auto;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-gift-link {
|
|
||||||
color: var(--primary);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.cl-gift-link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-gift-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
margin-bottom: 15px;
|
||||||
width: 55px; /* Ajusté légèrement */
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
.pf-child-header h3 {
|
||||||
/* Le Prix (Visible par défaut sur desktop) */
|
margin: 0;
|
||||||
.cl-gift-amount {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #059669;
|
|
||||||
background: #ecfdf5;
|
|
||||||
padding: 1px 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
transition:
|
|
||||||
opacity 0.2s,
|
|
||||||
transform 0.2s;
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Les Actions (Masquées par défaut sur desktop) */
|
|
||||||
.cl-gift-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.8);
|
|
||||||
pointer-events: none;
|
|
||||||
transition:
|
|
||||||
opacity 0.2s,
|
|
||||||
transform 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* === L'EFFET SWAP (Optimisé) === */
|
|
||||||
.cl-gift-line:hover .cl-gift-amount {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.8);
|
|
||||||
}
|
|
||||||
.cl-gift-line:hover .cl-gift-actions {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Boutons d'action */
|
|
||||||
.cl-gift-action-btn {
|
|
||||||
background: #f1f5f9;
|
|
||||||
border: 1px solid #cbd5e1;
|
|
||||||
width: 26px; /* Légèrement agrandi pour le tactile */
|
|
||||||
height: 26px;
|
|
||||||
border-radius: 6px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #475569;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 0;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
.cl-gift-action-btn:hover {
|
|
||||||
background: white;
|
|
||||||
border-color: var(--text-main);
|
|
||||||
}
|
|
||||||
.cl-gift-delete:hover {
|
|
||||||
color: #dc2626;
|
|
||||||
border-color: #dc2626;
|
|
||||||
background: #fef2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- 8. BUDGET & TRICOUNT --- */
|
|
||||||
.pf-section--panel {
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
.pf-section--panel h2 {
|
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
margin-bottom: 16px;
|
|
||||||
border-left: 5px solid var(--primary);
|
|
||||||
padding-left: 12px;
|
|
||||||
color: var(--text-main);
|
color: var(--text-main);
|
||||||
background: white;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-budget-wrapper {
|
/* === BARRE DES TOTAUX (PILLS) === */
|
||||||
background: white;
|
.pf-child-totals-bar {
|
||||||
border-radius: var(--radius-card);
|
display: flex;
|
||||||
box-shadow: var(--shadow-card);
|
gap: 8px;
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
-webkit-overflow-scrolling: touch; /* Scroll fluide sur iOS */
|
padding-bottom: 12px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
|
.pf-child-totals-bar::-webkit-scrollbar {
|
||||||
.pf-table {
|
display: none; /* Chrome/Safari */
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
}
|
||||||
|
.pf-summary-pill {
|
||||||
.pf-table th,
|
white-space: nowrap;
|
||||||
.pf-table td {
|
|
||||||
padding: 10px 14px;
|
|
||||||
border-bottom: 1px solid var(--border-light);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
.pf-table th {
|
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
font-weight: 600;
|
padding: 4px 12px;
|
||||||
text-align: left;
|
border-radius: 50px;
|
||||||
color: var(--text-muted);
|
font-size: 0.8rem;
|
||||||
}
|
|
||||||
.cl-debt-matrix td {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-mtx-owe {
|
|
||||||
color: #dc2626;
|
|
||||||
background: #fef2f2;
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
color: #475569;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Matrice Tricount - Colonne Sticky */
|
/* === FEED DES CARTES CADEAUX === */
|
||||||
|
.pf-gift-feed {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
}
|
||||||
|
.pf-gift-card-compact {
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
padding: 12px;
|
||||||
|
position: relative;
|
||||||
|
transition: 0.2s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
.pf-gift-card-compact:hover {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
.pf-gift-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.pf-gift-link {
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
.pf-gift-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
.pf-gift-price {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--success);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.pf-gift-badges-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.pf-pill-adult {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.pf-pill-occ {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
font-weight: 700;
|
||||||
|
background: #fef3c7;
|
||||||
|
color: #b45309;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.pf-gift-payer {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #ef4444;
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.pf-gift-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === TRICOUNT / LIQUIDATIONS === */
|
||||||
|
.pf-tricount-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.pf-tricount-item {
|
||||||
|
background: white;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
.cl-debt-matrix th:first-child,
|
.cl-debt-matrix th:first-child,
|
||||||
.cl-debt-matrix td:first-child {
|
.cl-debt-matrix td:first-child {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: #f8fafc;
|
background: var(--bg-page);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border-right: 2px solid var(--border-light);
|
border-right: 2px solid var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 9. MODALES --- */
|
/* Icônes des fêtes (Tió, Noël, etc.) plus petites et bien alignées */
|
||||||
.cl-modal {
|
.cl-occasion-icon {
|
||||||
display: none;
|
width: 20px; /* Réduit pour être discret */
|
||||||
|
height: 20px;
|
||||||
|
object-fit: contain;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.cl-modal.cl-open {
|
|
||||||
|
.cl-occasion-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 8px; /* Espace réduit entre l'icône et le texte */
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
.cl-modal-backdrop {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(15, 23, 42, 0.5);
|
|
||||||
backdrop-filter: blur(2px);
|
|
||||||
}
|
|
||||||
.cl-modal-dialog {
|
|
||||||
position: relative;
|
|
||||||
background: white;
|
|
||||||
width: min(450px, 90vw);
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
||||||
z-index: 10;
|
|
||||||
padding: 0;
|
|
||||||
animation: modalPop 0.2s ease-out;
|
|
||||||
}
|
|
||||||
@keyframes modalPop {
|
|
||||||
from {
|
|
||||||
transform: scale(0.95);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: scale(1);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cl-modal-form {
|
|
||||||
padding: 24px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.cl-modal-form h3 {
|
|
||||||
margin: 0 0 12px;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: var(--text-main);
|
|
||||||
}
|
|
||||||
.clm-label {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: #475569;
|
|
||||||
}
|
|
||||||
.clm-label input,
|
|
||||||
.clm-label select {
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid #cbd5e1;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
background: #f8fafc;
|
|
||||||
}
|
|
||||||
.clm-label input:focus,
|
|
||||||
.clm-label select:focus {
|
|
||||||
background: white;
|
|
||||||
border-color: var(--primary);
|
|
||||||
outline: 2px solid rgba(37, 99, 235, 0.2);
|
|
||||||
}
|
|
||||||
.cl-modal-actions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.clm-cancel {
|
|
||||||
background: white;
|
|
||||||
border: 1px solid #cbd5e1;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
.clm-ok {
|
|
||||||
background: var(--primary);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 20px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 10. OPTIMISATIONS MOBILES ET TACTILES --- */
|
@media (max-width: 768px) {
|
||||||
|
.pf-child-section {
|
||||||
@media (hover: none) {
|
padding: 15px;
|
||||||
/* Sur écran tactile, on annule l'effet de swap */
|
|
||||||
.cl-gift-line {
|
|
||||||
flex-direction: column; /* On empile texte et prix/boutons */
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
}
|
||||||
|
.pf-filter-bar {
|
||||||
.cl-gift-amount {
|
padding: 0 5px;
|
||||||
opacity: 1 !important;
|
|
||||||
transform: none !important;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
|
.pf-filter-label {
|
||||||
.cl-gift-right {
|
display: none;
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between; /* Prix à gauche, boutons à droite */
|
|
||||||
margin-top: 4px;
|
|
||||||
border-top: 1px dashed var(--border-light);
|
|
||||||
padding-top: 4px;
|
|
||||||
}
|
}
|
||||||
|
.pf-gift-feed {
|
||||||
.cl-gift-actions {
|
grid-template-columns: 1fr 1fr;
|
||||||
position: relative;
|
}
|
||||||
opacity: 1 !important;
|
.pf-gift-title {
|
||||||
transform: none !important;
|
font-size: 0.85rem;
|
||||||
pointer-events: auto !important;
|
}
|
||||||
|
.pf-gift-price {
|
||||||
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media (max-width: 400px) {
|
||||||
@media (max-width: 1000px) {
|
.pf-gift-feed {
|
||||||
.cl-view-nadal .cl-occasion-children-tables,
|
|
||||||
.cl-view-anniversary .cl-occasion-children-tables {
|
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
.cl-view-anniversary .child-pol,
|
|
||||||
.cl-view-anniversary .child-pep,
|
|
||||||
.cl-view-anniversary .child-elna,
|
|
||||||
.cl-view-anniversary .child-bru,
|
|
||||||
.cl-view-anniversary .child-guim {
|
|
||||||
grid-column: span 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transformation des tableaux en mode "Carrousel" horizontal */
|
|
||||||
.cl-child-table {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
scroll-snap-type: x mandatory; /* Effet de magnétisme */
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
padding-bottom: 10px; /* Espace pour la barre de scroll */
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-child-table caption {
|
|
||||||
/* Garde le nom de l'enfant visible même en scrollant à droite */
|
|
||||||
position: sticky;
|
|
||||||
left: 0;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-child-table th,
|
|
||||||
.cl-child-table td {
|
|
||||||
min-width: 180px; /* Plus large pour la lisibilité tactile */
|
|
||||||
scroll-snap-align: start; /* S'arrête pile sur la colonne */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user