Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

1.0.9 (Unreleased)
------------------
- Enh: Competition view auto-scrolls to the last finished match above a live match.
- Enh: "Show all tips" modal reuses the match card header instead of a separate pairing/result line.
- Fix: Team flag badges drop their white background, removing the white box around flags on live match cards.

1.0.8 (June 12, 2026)
---------------------
- Fix: Undrawn knockout fixtures no longer mark the sync as failed, which had blocked point calculation for the whole competition.
Expand Down
8 changes: 8 additions & 0 deletions resources/css/kickoff.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@
background: #fff;
transition: border-color 0.2s;
}
/* Fixed area above the scrollable modal body holding the match-card preview. */
.kickoff-modal-subheader {
padding: 1rem 1rem 0;
}
.kickoff-modal-subheader .kickoff-match-card { margin-bottom: 1rem; }
.kickoff-match-card.is-tipped { border-left: 3px solid #28a745; }
.kickoff-match-card-meta {
display: grid; grid-template-columns: 1fr auto 1fr;
Expand Down Expand Up @@ -212,6 +217,9 @@
outline: 1px solid rgba(0, 0, 0, 0.12);
outline-offset: -1px;
}
.kickoff-team-badge--flag img {
background: transparent;
}
.kickoff-match-score {
display: flex; align-items: center; gap: 4px;
flex-shrink: 0;
Expand Down
15 changes: 15 additions & 0 deletions views/competition/_detail_modal.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,20 @@
var modalEl = document.getElementById('kickoff-detail-modal');
if (!modalEl) return;
var \$body = \$(modalEl).find('.modal-body');
var \$subheader = \$(modalEl).find('[data-modal-subheader]');
var loadingHtml = '<p class="text-muted text-center">…</p>';

// The loaded content may carry a [data-modal-preview] block (e.g. the
// match card on the tips modal). Lift it out of the scrollable body
// into the fixed sub-header so it stays pinned above the scroll.
function relocatePreview() {
\$subheader.empty().hide();
var \$preview = \$body.find('[data-modal-preview]').first();
if (\$preview.length) {
\$subheader.append(\$preview).show();
}
}

// Rebind on every script run (HumHub Pjax can re-execute registered JS).
// The namespaced .off() is the cheap guard against accumulating handlers.
\$(document).off('click.kickoffDetailModal');
Expand All @@ -23,12 +35,14 @@
var titleAttr = \$(this).attr('data-modal-title');
if (!url) return;
\$body.html(loadingHtml);
\$subheader.empty().hide();
if (titleAttr) {
\$(modalEl).find('.modal-title').text(titleAttr);
}
var isShown = modalEl.classList.contains('show');
\$.get(url).done(function (html) {
\$body.html(html);
relocatePreview();
}).fail(function () {
\$body.html('<p class="text-danger">Could not load.</p>');
});
Expand Down Expand Up @@ -58,6 +72,7 @@
<h5 class="modal-title">&nbsp;</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="kickoff-modal-subheader" data-modal-subheader style="display:none;"></div>
<div class="modal-body">
<p class="text-muted text-center"><?= Yii::t('KickoffModule.base', 'Loading…') ?></p>
</div>
Expand Down
9 changes: 6 additions & 3 deletions views/competition/_match_card.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
/** @var bool $hasTips */
/** @var bool $showOtherTipsLink */
/** @var \humhub\modules\kickoff\models\Competition $competition */
/** @var bool $preview Render only the part above the footer (no dashed-line footer). */

$preview = $preview ?? false;

$home = $game->homeTeam;
$away = $game->awayTeam;
Expand Down Expand Up @@ -60,7 +63,7 @@
}

?>
<div class="kickoff-match-card<?= $isTipped && $canTip ? ' is-tipped' : '' ?><?= $isLive ? ' is-live' : '' ?>" data-game-id="<?= (int) $game->id ?>">
<div class="kickoff-match-card<?= $isTipped && $canTip ? ' is-tipped' : '' ?><?= $isLive ? ' is-live' : '' ?><?= $isFinished ? ' is-finished' : '' ?>" data-game-id="<?= (int) $game->id ?>">
<div class="kickoff-match-card-meta">
<span class="kickoff-match-card-meta-time">
<?= Html::encode($kickoffDate) ?>
Expand Down Expand Up @@ -143,7 +146,7 @@ class="form-control form-control-sm text-center kickoff-score-input"
$hasFooterVenue = !empty($game->venue);
$hasFooterActions = !empty($showOtherTipsLink);
?>
<?php if ($hasFooterTip || $hasFooterVenue || $hasFooterActions): ?>
<?php if (!$preview && ($hasFooterTip || $hasFooterVenue || $hasFooterActions)): ?>
<div class="kickoff-match-card-footer">
<div class="kickoff-match-card-footer-tip">
<?php if ($hasFooterTip): ?>
Expand Down Expand Up @@ -180,7 +183,7 @@ class="form-control form-control-sm text-center kickoff-score-input"
<a href="#"
data-kickoff-modal
data-modal-url="<?= \yii\helpers\Url::to(['/kickoff/competition/match-tips', 'slug' => $competition->slug, 'gameId' => $game->id]) ?>"
data-modal-title="<?= Html::encode(($home ? $home->getDisplayName() : '?') . ' – ' . ($away ? $away->getDisplayName() : '?')) ?>">
data-modal-title="<?= Html::encode(Yii::t('KickoffModule.base', 'All placed tips')) ?>">
<?= Yii::t('KickoffModule.base', 'Show all tips') ?> →
</a>
<?php else: ?>
Expand Down
32 changes: 12 additions & 20 deletions views/competition/_match_tips.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php

use humhub\modules\kickoff\models\Game;
use humhub\modules\kickoff\services\KickoffTime;
use humhub\modules\user\widgets\Image as UserImage;
use yii\helpers\Html;
use yii\helpers\Url;
Expand Down Expand Up @@ -30,26 +29,19 @@
return 'points-zero';
};

$home = $game->homeTeam ? $game->homeTeam->getDisplayName() : '?';
$away = $game->awayTeam ? $game->awayTeam->getDisplayName() : '?';
$isFinished = $game->isFinished() && $game->home_score !== null && $game->away_score !== null;

?>
<p class="mb-2">
<strong><?= Html::encode($home) ?></strong>
<?php if ($isFinished): ?>
<span class="text-muted">
<?= (int) $game->home_score ?>:<?= (int) $game->away_score ?>
</span>
<?php else: ?>
<span class="text-muted">–</span>
<?php endif; ?>
<strong><?= Html::encode($away) ?></strong>
<small class="text-muted">
<?php $kickoffEpoch = KickoffTime::parse($game->kickoff_at); ?>
<?= $kickoffEpoch !== null ? Yii::$app->formatter->asDatetime($kickoffEpoch, 'short') : '' ?>
</small>
</p>
<div data-modal-preview>
<?= $this->render('_match_card', [
'game' => $game,
'tip' => null,
'editable' => false,
'canParticipate' => false,
'hasTips' => false,
'showOtherTipsLink' => false,
'competition' => $competition,
'preview' => true,
]) ?>
</div>

<?php if ($totalCount === 0): ?>
<p class="text-muted"><?= Yii::t('KickoffModule.base', 'No tips placed on this match.') ?></p>
Expand Down
23 changes: 23 additions & 0 deletions views/competition/view.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,29 @@ function flash(\$inputs, color) {
JS;
$this->registerJs($specialBetAutosaveJs, \yii\web\View::POS_END, 'kickoff-special-bet-autosave');

// When the matchday has a live match, scroll the view so the last finished
// match right above it is at the top — putting the live action front and
// centre without hiding what just happened before it.
$liveScrollJs = <<<JS
(function (\$) {
\$(function () {
var \$live = \$('.kickoff-match-card.is-live').first();
if (!\$live.length) return;
// One-shot: only scroll the first time this live card appears. Guards
// against HumHub Pjax re-executing this block and yanking the viewport
// away from wherever the user has scrolled. A fresh matchday load swaps
// in a new card without the flag, so a genuinely new view still scrolls.
if (\$live.data('kickoffScrolled')) return;
\$live.data('kickoffScrolled', true);
var \$target = \$live.prevAll('.kickoff-match-card.is-finished').first();
if (!\$target.length) \$target = \$live;
var top = \$target.offset().top - 70;
\$('html, body').animate({ scrollTop: top < 0 ? 0 : top }, 400);
});
})(jQuery);
JS;
$this->registerJs($liveScrollJs, \yii\web\View::POS_END, 'kickoff-live-scroll');


?>
<div class="container">
Expand Down
Loading