Files
fitme3070.pt/database/migration_20260926_gyms.sql
T
andreandClaude Opus 5.5 5e300fc341 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>
2026-09-25 07:26:40 +01:00

55 lines
4.0 KiB
SQL

-- ─── 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`);