centralise repertoire filter column rendering

- shared repFilterEntry() and  config array
- shared repFilterEntry() and $filterColumns config array
- fix single-valued FK fading via full intersection
This commit is contained in:
Pontoporeia
2026-05-05 18:27:47 +02:00
parent bca707ee96
commit b063312642
6 changed files with 121 additions and 177 deletions

View File

@@ -16,9 +16,8 @@ $activeSets = [
'kw' => $activeFilters['kw'] ?? [],
];
// Build the student map from matched students only
// name => [id, id, ...] (a student may have multiple theses)
$studentWorks = []; // name => [thesis ids]
// ── Students ────────────────────────────────────────────────────────────────
$studentWorks = [];
foreach ($repData['students'] as $s) {
if (empty($s['authors'])) continue;
foreach (explode(',', $s['authors']) as $name) {
@@ -28,15 +27,9 @@ foreach ($repData['students'] as $s) {
}
}
ksort($studentWorks);
// Legacy alias for single-id use
$studentMap = array_map(fn($ids) => $ids[0], $studentWorks);
// ── Shared helpers ──────────────────────────────────────────────────────────
/**
* Build the toggle URL for a filter button.
* Toggles $value in $dim; keeps all other active filters intact.
*/
function repToggleUrl(array $sets, string $dim, string $value): string {
if (in_array($value, $sets[$dim], true)) {
$sets[$dim] = array_values(array_filter($sets[$dim], fn($v) => $v !== $value));
@@ -53,123 +46,64 @@ function repToggleUrl(array $sets, string $dim, string $value): string {
return '/repertoire' . ($qs ? '?' . $qs : '');
}
function repFilterEntry(
array $item,
string $dim,
array $activeSets,
bool $anyActive,
bool $colHasMatch,
string $hx,
): void {
$val = (string)$item['value'];
$isActive = in_array($val, $activeSets[$dim], true);
$isFaded = $anyActive && $colHasMatch && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, $dim, $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php
}
// ── Column definitions ──────────────────────────────────────────────────────
$hx = 'hx-target="#repertoire-index" hx-swap="outerHTML" hx-push-url="true" hx-indicator="#rep-indicator"';
$anyActive = !empty($activeSets['years']) || !empty($activeSets['ap'])
|| !empty($activeSets['or']) || !empty($activeSets['fi'])
|| !empty($activeSets['kw']);
// Per-column: does this dimension have ANY matched entry in the current result set?
// When a column has zero matched entries despite other filters being active, it means
// no thesis in the matched set carries that FK — the column has no useful cross-filter
// signal and its entries must NOT be faded (the user may still want to select them).
$colHasMatches = [
'years' => !empty(array_filter($repData['years'], fn($i) => $i['matched'])),
'ap' => !empty(array_filter($repData['ap_programs'], fn($i) => $i['matched'])),
'or' => !empty(array_filter($repData['orientations'], fn($i) => $i['matched'])),
'fi' => !empty(array_filter($repData['finality_types'], fn($i) => $i['matched'])),
'kw' => !empty(array_filter($repData['keywords'], fn($i) => $i['matched'])),
$filterColumns = [
['dataKey' => 'years', 'dim' => 'years', 'heading' => 'Années'],
['dataKey' => 'ap_programs', 'dim' => 'ap', 'heading' => 'Ateliers Pluridisciplinaires'],
['dataKey' => 'orientations', 'dim' => 'or', 'heading' => 'Orientations'],
['dataKey' => 'finality_types', 'dim' => 'fi', 'heading' => 'Finalité du Master'],
['dataKey' => 'keywords', 'dim' => 'kw', 'heading' => 'Mots-clés'],
];
// Common HTMX attributes for all active filter buttons
$hx = 'hx-target="#repertoire-index" hx-swap="outerHTML" hx-push-url="true" hx-indicator="#rep-indicator"';
$colHasMatches = [];
foreach ($filterColumns as $col) {
$colHasMatches[$col['dim']] = !empty(array_filter(
$repData[$col['dataKey']],
fn($i) => $i['matched']
));
}
?>
<div id="repertoire-index" class="repertoire-index">
<!-- ANNÉES -->
<section class="repertoire-col" data-col="years">
<h2>Années</h2>
<ul>
<?php foreach ($repData['years'] as $item):
$val = (string)$item['value'];
$isActive = in_array($val, $activeSets['years'], true);
$isFaded = $anyActive && $colHasMatches['years'] && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, 'years', $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php endforeach; ?>
</ul>
</section>
<!-- ATELIERS PLURIDISCIPLINAIRES -->
<section class="repertoire-col" data-col="ap">
<h2>Ateliers Pluridisciplinaires</h2>
<ul>
<?php foreach ($repData['ap_programs'] as $item):
$val = $item['value'];
$isActive = in_array($val, $activeSets['ap'], true);
$isFaded = $anyActive && $colHasMatches['ap'] && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, 'ap', $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php endforeach; ?>
</ul>
</section>
<!-- ORIENTATIONS -->
<section class="repertoire-col" data-col="or">
<h2>Orientations</h2>
<ul>
<?php foreach ($repData['orientations'] as $item):
$val = $item['value'];
$isActive = in_array($val, $activeSets['or'], true);
$isFaded = $anyActive && $colHasMatches['or'] && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, 'or', $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php endforeach; ?>
</ul>
</section>
<!-- FINALITÉ DU MASTER -->
<section class="repertoire-col" data-col="fi">
<h2>Finalité du Master</h2>
<ul>
<?php foreach ($repData['finality_types'] as $item):
$val = $item['value'];
$isActive = in_array($val, $activeSets['fi'], true);
$isFaded = $anyActive && $colHasMatches['fi'] && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, 'fi', $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php endforeach; ?>
</ul>
</section>
<?php
// Render filter columns in the correct left-to-right order.
// Students column (non-filter) is inserted between keywords and AP/or/fi/years.
$renderOrder = ['years', 'ap', 'or', 'fi', 'students', 'kw'];
foreach ($renderOrder as $colKey):
if ($colKey === 'students'): ?>
<!-- ÉTUDIANTES -->
<section class="repertoire-col" data-col="students">
<h2>Étudiantes</h2>
@@ -198,29 +132,17 @@ $hx = 'hx-target="#repertoire-index" hx-swap="outerHTML" hx-push-url="true" hx-i
<?php endif; ?>
</ul>
</section>
<!-- MOTS-CLÉS -->
<section class="repertoire-col" data-col="kw">
<h2>Mots-clés</h2>
<?php else:
$col = array_values(array_filter($filterColumns, fn($c) => $c['dim'] === $colKey))[0]; ?>
<section class="repertoire-col" data-col="<?= $col['dim'] ?>">
<h2><?= htmlspecialchars($col['heading']) ?></h2>
<ul>
<?php foreach ($repData['keywords'] as $item):
$val = $item['value'];
$isActive = in_array($val, $activeSets['kw'], true);
$isFaded = $anyActive && $colHasMatches['kw'] && !$item['matched'] && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, 'kw', $val);
?>
<li>
<button type="button" class="<?= $cls ?>"
aria-pressed="<?= $isActive ? 'true' : 'false' ?>"
<?= $isFaded ? 'disabled' : "hx-get=\"" . htmlspecialchars($url) . "\" $hx" ?>>
<?= htmlspecialchars($val) ?>
</button>
</li>
<?php endforeach; ?>
<?php foreach ($repData[$col['dataKey']] as $item):
repFilterEntry($item, $col['dim'], $activeSets, $anyActive, $colHasMatches[$col['dim']], $hx);
endforeach; ?>
</ul>
</section>
<?php endif;
endforeach; ?>
</div>