Files
andreandClaude Opus 5.5 f95c9ebc52 feat: contactos editáveis, equipa com título/aptidões, mensalidades e reviews Google
- Contactos: nova página no backoffice (fitme3070_info) e 964 201 137 como
  telefone principal; o telefone secundário só aparece quando existe.
- Equipa: título, aptidões/funções e formações; o cartão mostra foto, nome e
  título e abre um cartão horizontal de detalhe (hover no desktop, toque no
  telemóvel) sem mexer na grelha.
- Mensalidades: cartões editáveis com um destaque, modal de inscrição que
  guarda o pedido (RGPD + estado + campos reservados para o CRM) e avisa o
  ginásio por e-mail; lista de pedidos com estados, notas e CSV.
- Reviews Google via Places API (New), filtradas por classificação mínima e
  em cache configurável; página de configuração com pesquisa do Place ID.
- Assets com ?v=<mtime>: site/assets é servido com cache imutável de 1 ano.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 14:58:57 +01:00

87 lines
4.2 KiB
PHP

<?php
// ─── CodeIgniter 4 view-helper compatibility shim ────────────────────────────
// Lets the original fitme3070 view partials (site/partials/*) run byte-for-byte
// under the new plain-PHP structure. Only the helpers the ported templates
// actually use are implemented. base_url() comes from config/functions.php.
if (!defined('SITE_INCLUDES')) {
define('SITE_INCLUDES', dirname(__DIR__) . '/site/includes');
}
// <script src> / <link rel=stylesheet> tag builders (CI4 html helper).
// site/assets is served with a one-year immutable Cache-Control, so local files
// carry their mtime as ?v= — an edited style.css/main.js gets a new URL instead
// of staying stuck in returning visitors' caches.
function _asset_versioned(string $path): string {
$file = dirname(__DIR__) . '/' . ltrim($path, '/');
if (strpos($path, '?') !== false || preg_match('~^https?://~', $path) || !is_file($file)) return base_url($path);
return base_url($path) . '?v=' . filemtime($file);
}
function script_tag(string $src): string {
return '<script src="' . htmlspecialchars(_asset_versioned($src), ENT_QUOTES) . '"></script>' . "\n";
}
function link_tag(string $href, string $rel = 'stylesheet'): string {
return '<link rel="' . $rel . '" href="' . htmlspecialchars(_asset_versioned($href), ENT_QUOTES) . '">' . "\n";
}
function site_url(string $path = ''): string { return base_url($path); }
function esc($value, string $context = 'html'): string {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
// Render a partial like CI4's view(): extract $data + the shared page vars
// (info / navbar / current_uri_2 via $GLOBALS['_view_shared']) and return the
// buffered HTML. Lets partials read $info without threading it through callers.
function view(string $name, array $data = []): string {
$shared = $GLOBALS['_view_shared'] ?? [];
extract($shared);
extract($data);
ob_start();
include SITE_INCLUDES . '/' . $name . '.php';
return ob_get_clean();
}
// Minimal session() shim — the templates only read getFlashdata().
// Per-request singleton: the constructor's flash sweep therefore runs once per
// request, which is what makes flashdata actually "flash".
function session(): _FitSession {
static $s = null;
if ($s === null) $s = new _FitSession();
return $s;
}
class _FitSession {
// CI4-style flashdata lifecycle. A value set with setFlashdata() in request N
// (the POST handler, just before its redirect) must be readable in request
// N+1 (the redirect target) and then disappear — otherwise the banner sticks
// around forever, surviving reloads, because nothing ever clears it.
//
// We track an age marker per key: setFlashdata() marks it 'new'; on the next
// request the sweep ages 'new' → 'old' (still readable), and on the request
// after that it deletes anything 'old'. The sweep runs once, here, before any
// read or write this request (setFlashdata() is itself the first session()
// call in a POST handler, so the constructor always precedes it).
public function __construct() {
foreach ($_SESSION['_flash_age'] ?? [] as $k => $age) {
if ($age === 'old') {
unset($_SESSION['_flash'][$k], $_SESSION['_flash_age'][$k]);
} else {
$_SESSION['_flash_age'][$k] = 'old';
}
}
// Sweep orphans left by the previous (never-expiring) shim: any flash
// value with no age marker is stale and would otherwise stick forever.
foreach (array_keys($_SESSION['_flash'] ?? []) as $k) {
if (!isset($_SESSION['_flash_age'][$k])) unset($_SESSION['_flash'][$k]);
}
if (empty($_SESSION['_flash'])) unset($_SESSION['_flash']);
if (empty($_SESSION['_flash_age'])) unset($_SESSION['_flash_age']);
}
public function getFlashdata(string $key) { return $_SESSION['_flash'][$key] ?? null; }
public function setFlashdata(string $key, $val): void {
$_SESSION['_flash'][$key] = $val;
$_SESSION['_flash_age'][$key] = 'new';
}
public function get(string $key) { return $_SESSION[$key] ?? null; }
public function set(string $key, $val): void { $_SESSION[$key] = $val; }
}