feat: vários ginásios (/3070/ e /<código postal>/), CRUD de ginásios, modalidades após a galeria
Estrutura multi-ginásio: as mesmas secções do site, conteúdo próprio por ginásio, e cada um numa pasta da URL com o código postal da zona. - migration_20260926_gyms: tabela gyms (pasta, nome, localidade, publicado, principal, secções visíveis, título/imagem do header, SEO, galeria, Place ID, página CrossFit) com o FitMe 3070 como ginásio 1 em /3070/; gym_id em team, modalidades (o horário segue a modalidade), work_hours, memberships, membership_leads e website_offer (tudo o existente → ginásio 1); events com gym_id NULL = todos; fitme3070_info passa a uma linha por ginásio (id = gym). - Site: /<pasta>/, /<pasta>/crossfit, /<pasta>/agendar|inscricao. Ginásio por publicar → 404 para o público, pré-visualização (com faixa) para a equipa. / e /home → 301 para o ginásio principal (a manter a query); com mais de um ginásio publicado, a raiz passa a uma página para escolher. Secções desligadas não aparecem nem no menu; menu, canonical, JSON-LD, sitemap, formulários, e-mails e reviews (Place ID e cache) são do ginásio. - Backoffice: Gestão → Ginásios (criar/editar/apagar, secções, e «começar com o conteúdo de» outro ginásio — cópias independentes) e o seletor «A editar» no topo; equipa, modalidades, horários, horário de funcionamento, mensalidades, pedidos, contactos e reviews trabalham sobre o ginásio escolhido, e um registo de outro ginásio não abre. - Modalidades passam para logo a seguir à galeria das instalações. - Galeria sem cantos arredondados (raio da marca). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f8f68c61a
commit
5e300fc341
33 files changed
+1146
-197
No files matched your search
@@ -0,0 +1,54 @@
|
||||
-- ─── Vários ginásios ─────────────────────────────────────────────────────────
|
||||
-- O site passa a servir mais do que um ginásio com as mesmas secções e só o
|
||||
-- conteúdo a mudar. Cada ginásio vive numa pasta da URL (o código postal da
|
||||
-- zona: /3070/, /XXXX/) e pode estar por publicar (só a equipa o vê).
|
||||
--
|
||||
-- gyms: identidade, pasta, secções visíveis e textos próprios de cada um.
|
||||
-- gym_id: o conteúdo existente passa a pertencer a um ginásio. Tudo o que já
|
||||
-- existe fica no ginásio 1 (FitMe 3070). O horário de aulas segue a
|
||||
-- modalidade (horario.id_modalidade), por isso não precisa de coluna própria.
|
||||
-- fitme3070_info: uma linha por ginásio, com id = gyms.id.
|
||||
-- Safe to re-run.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `gyms` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`slug` VARCHAR(20) NOT NULL, -- pasta da URL, ex.: 3070
|
||||
`name` VARCHAR(100) NOT NULL, -- ex.: FitMe 3070
|
||||
`locality` VARCHAR(100) NOT NULL DEFAULT '', -- ex.: Mira
|
||||
`postal_code` VARCHAR(20) NOT NULL DEFAULT '',
|
||||
`is_published` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`is_default` TINYINT(1) NOT NULL DEFAULT 0, -- para onde vai a raiz / e /home
|
||||
`sections` TEXT DEFAULT NULL, -- JSON: secções visíveis (NULL = todas)
|
||||
`hero_title` VARCHAR(150) NOT NULL DEFAULT '',
|
||||
`hero_subtitle` VARCHAR(150) NOT NULL DEFAULT '',
|
||||
`hero_image` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`seo_title` VARCHAR(160) NOT NULL DEFAULT '',
|
||||
`seo_description` VARCHAR(300) NOT NULL DEFAULT '',
|
||||
`gallery_id` INT(11) DEFAULT NULL, -- galleries.id → uploads/gallery/<id>/
|
||||
`google_place_id` VARCHAR(120) NOT NULL DEFAULT '',
|
||||
`has_crossfit` TINYINT(1) NOT NULL DEFAULT 0, -- mostra a página /<pasta>/crossfit
|
||||
`item_order` INT(11) NOT NULL DEFAULT 0,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_gyms_slug` (`slug`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- O ginásio atual. Place ID das reviews e galeria vêm do que já existe.
|
||||
INSERT INTO `gyms` (`id`, `slug`, `name`, `locality`, `postal_code`, `is_published`, `is_default`,
|
||||
`hero_title`, `hero_subtitle`, `hero_image`, `gallery_id`, `google_place_id`, `has_crossfit`)
|
||||
SELECT 1, '3070', 'FitMe 3070', 'Mira', '3070', 1, 1,
|
||||
'Ginásio, Box, Aulas', '& Acompanhamento', 'site/assets/img/home.jpg', 1,
|
||||
COALESCE((SELECT `setting_value` FROM `system_settings` WHERE `setting_key` = 'google_place_id'), ''), 1
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `gyms`);
|
||||
|
||||
ALTER TABLE `team` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `id`, ADD KEY IF NOT EXISTS `idx_team_gym` (`gym_id`);
|
||||
ALTER TABLE `modalidades` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `m_id`, ADD KEY IF NOT EXISTS `idx_modalidades_gym` (`gym_id`);
|
||||
ALTER TABLE `work_hours` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `id`, ADD KEY IF NOT EXISTS `idx_work_hours_gym` (`gym_id`);
|
||||
ALTER TABLE `memberships` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `id`, ADD KEY IF NOT EXISTS `idx_memberships_gym` (`gym_id`);
|
||||
ALTER TABLE `membership_leads` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `id`, ADD KEY IF NOT EXISTS `idx_membership_leads_gym` (`gym_id`);
|
||||
ALTER TABLE `website_offer` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) NOT NULL DEFAULT 1 AFTER `o_id`, ADD KEY IF NOT EXISTS `idx_website_offer_gym` (`gym_id`);
|
||||
-- Eventos: NULL = todos os ginásios.
|
||||
ALTER TABLE `events` ADD COLUMN IF NOT EXISTS `gym_id` INT(11) DEFAULT NULL AFTER `e_id`, ADD KEY IF NOT EXISTS `idx_events_gym` (`gym_id`);
|
||||
Reference in new issue
Block a user