diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b919835 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.claude/ +.mcp.json +TODO.md +*.min.css +*.min.js diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bfcc00a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ + + +# Changelog + +## [Unreleased] + +### Added +- **Repository** -- initial repo creation with scaffolding +- **System Plugin** -- Extension class, service provider +- **SQL Schema** -- 9 tables +- **Admin Component** -- 6 views: Dashboard, Members, Memberships, Classes, Trainers, Equipment +- **Webservices Plugin** -- 6 API routes +- **Configuration** -- basic settings fieldset +- **Access Control** -- core permissions +- **Language Files** -- en-GB translations diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..69ba425 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,30 @@ +# MokoSuiteGym + +Layer 2 — Fitness center management, memberships, class scheduling, trainer management, equipment tracking + +## Quick Reference + +| Field | Value | +|---|---| +| **Package** | `pkg_mokosuitegym` | +| **Layer** | 2 (requires: Client, CRM) | +| **Language** | PHP 8.3+ | +| **Branch** | develop on `dev`, merge to `main` (protected) | + +## Architecture + +Joomla **package** -- Layer 2 add-on. CRM contacts as gym members and trainers, membership tiers, class scheduling with enrollment, and equipment maintenance. + +## Rules + +- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js` +- **Attribution**: `Authored-by: Moko Consulting` +- **Workflow directory**: `.mokogitea/` +- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoCLI/wiki) +- **Changelog**: `[Unreleased]` only -- release system assigns versions + +## Coding Standards + +- PHP 8.3+ / Joomla 6 patterns +- `$this->getDatabase()` in models, `Factory::getContainer()->get(DatabaseInterface::class)` in helpers +- `Factory::getApplication()->getIdentity()` for user diff --git a/source/packages/com_mokosuitegym/admin/access.xml b/source/packages/com_mokosuitegym/admin/access.xml new file mode 100644 index 0000000..5485090 --- /dev/null +++ b/source/packages/com_mokosuitegym/admin/access.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + +
+
diff --git a/source/packages/com_mokosuitegym/admin/config.xml b/source/packages/com_mokosuitegym/admin/config.xml new file mode 100644 index 0000000..6c7aef9 --- /dev/null +++ b/source/packages/com_mokosuitegym/admin/config.xml @@ -0,0 +1,6 @@ + + +
+ +
+
diff --git a/source/packages/com_mokosuitegym/admin/services/provider.php b/source/packages/com_mokosuitegym/admin/services/provider.php new file mode 100644 index 0000000..a7e02a6 --- /dev/null +++ b/source/packages/com_mokosuitegym/admin/services/provider.php @@ -0,0 +1,30 @@ +set( + ComponentInterface::class, + function (Container $container) { + $component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class)); + $component->setMVCFactory($container->get(MVCFactoryInterface::class)); + return $component; + } + ); + } +}; diff --git a/source/packages/com_mokosuitegym/mokosuitegym.xml b/source/packages/com_mokosuitegym/mokosuitegym.xml new file mode 100644 index 0000000..60a0804 --- /dev/null +++ b/source/packages/com_mokosuitegym/mokosuitegym.xml @@ -0,0 +1,32 @@ + + + com_mokosuitegym + 0.0.0 + 2026-06 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + (C) 2026 Moko Consulting + GPL-3.0-or-later + Layer 2 — Fitness center management, memberships, class scheduling, trainer management, equipment tracking + MokoConsulting\Component\MokoSuiteGym + + + services + src + tmpl + language + access.xml + config.xml + + MokoSuiteGym + + Dashboard + Members + Memberships + Classes + Trainers + Equipment + + + diff --git a/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.ini b/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.ini new file mode 100644 index 0000000..1059f2d --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.ini @@ -0,0 +1,3 @@ +PLG_SYSTEM_MOKOSUITEGYM="Gym" +PLG_SYSTEM_MOKOSUITEGYM_DESCRIPTION="MokoSuiteGym system plugin" +PLG_SYSTEM_MOKOSUITEGYM_ENABLED="Enable MokoSuiteGym" diff --git a/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.sys.ini b/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.sys.ini new file mode 100644 index 0000000..1059f2d --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/language/en-GB/plg_system_mokosuitegym.sys.ini @@ -0,0 +1,3 @@ +PLG_SYSTEM_MOKOSUITEGYM="Gym" +PLG_SYSTEM_MOKOSUITEGYM_DESCRIPTION="MokoSuiteGym system plugin" +PLG_SYSTEM_MOKOSUITEGYM_ENABLED="Enable MokoSuiteGym" diff --git a/source/packages/plg_system_mokosuitegym/mokosuitegym.xml b/source/packages/plg_system_mokosuitegym/mokosuitegym.xml new file mode 100644 index 0000000..3d90a96 --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/mokosuitegym.xml @@ -0,0 +1,43 @@ + + + plg_system_mokosuitegym + 0.0.0 + 2026-06 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + (C) 2026 Moko Consulting + GPL-3.0-or-later + MokoSuiteGym system plugin -- schema owner and bootstrap + MokoConsulting\Plugin\System\MokoSuiteGym + + src + services + sql + language + + + + sql/install.mysql.sql + + + + + sql/uninstall.mysql.sql + + + + en-GB/plg_system_mokosuitegym.ini + en-GB/plg_system_mokosuitegym.sys.ini + + + +
+ + + + +
+
+
+
diff --git a/source/packages/plg_system_mokosuitegym/services/provider.php b/source/packages/plg_system_mokosuitegym/services/provider.php new file mode 100644 index 0000000..80dfbf4 --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/services/provider.php @@ -0,0 +1,31 @@ +set( + PluginInterface::class, + function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new Gym($dispatcher, (array) PluginHelper::getPlugin('system', 'mokosuitegym')); + $plugin->setApplication(Factory::getApplication()); + return $plugin; + } + ); + } +}; diff --git a/source/packages/plg_system_mokosuitegym/sql/install.mysql.sql b/source/packages/plg_system_mokosuitegym/sql/install.mysql.sql new file mode 100644 index 0000000..4ccb845 --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/sql/install.mysql.sql @@ -0,0 +1,181 @@ +-- MokoSuiteGym Schema +-- Copyright (C) 2026 Moko Consulting +-- SPDX-License-Identifier: GPL-3.0-or-later + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_memberships` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `tier` ENUM('basic','standard','premium','vip','student','senior') NOT NULL DEFAULT 'basic', + `duration_months` INT UNSIGNED NOT NULL DEFAULT 1, + `price` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `enrollment_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `guest_passes` INT UNSIGNED NOT NULL DEFAULT 0, + `class_access` TINYINT(1) NOT NULL DEFAULT 0, + `trainer_sessions` INT UNSIGNED NOT NULL DEFAULT 0, + `status` ENUM('active','inactive','archived') NOT NULL DEFAULT 'active', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_tier` (`tier`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_members` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `contact_id` INT UNSIGNED NULL COMMENT 'FK to CRM contacts', + `member_number` VARCHAR(50) NOT NULL, + `first_name` VARCHAR(255) NOT NULL, + `last_name` VARCHAR(255) NOT NULL, + `date_of_birth` DATE NULL, + `gender` ENUM('male','female','other') NULL, + `phone` VARCHAR(50) NULL, + `email` VARCHAR(255) NULL, + `emergency_contact_name` VARCHAR(255) NULL, + `emergency_contact_phone` VARCHAR(50) NULL, + `photo` VARCHAR(500) NULL, + `join_date` DATE NOT NULL, + `status` ENUM('active','inactive','frozen','cancelled') NOT NULL DEFAULT 'active', + `notes` TEXT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_member_number` (`member_number`), + KEY `idx_contact` (`contact_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_member_memberships` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INT UNSIGNED NOT NULL, + `membership_id` INT UNSIGNED NOT NULL, + `start_date` DATE NOT NULL, + `end_date` DATE NULL, + `auto_renew` TINYINT(1) NOT NULL DEFAULT 1, + `payment_method` ENUM('cash','card','bank_transfer','online') NULL, + `amount_paid` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `status` ENUM('active','expired','cancelled','frozen') NOT NULL DEFAULT 'active', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_member` (`member_id`), + KEY `idx_membership` (`membership_id`), + KEY `idx_status` (`status`), + KEY `idx_end_date` (`end_date`), + CONSTRAINT `fk_membermembership_member` FOREIGN KEY (`member_id`) REFERENCES `#__mokosuitegym_members`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_membermembership_membership` FOREIGN KEY (`membership_id`) REFERENCES `#__mokosuitegym_memberships`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_trainers` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `contact_id` INT UNSIGNED NULL COMMENT 'FK to CRM contacts', + `first_name` VARCHAR(255) NOT NULL, + `last_name` VARCHAR(255) NOT NULL, + `specializations` JSON NULL, + `certifications` JSON NULL, + `bio` TEXT NULL, + `phone` VARCHAR(50) NULL, + `email` VARCHAR(255) NULL, + `photo` VARCHAR(500) NULL, + `hourly_rate` DECIMAL(10,2) NULL, + `hire_date` DATE NULL, + `status` ENUM('active','inactive','on_leave') NOT NULL DEFAULT 'active', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_contact` (`contact_id`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_classes` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `class_type` ENUM('yoga','pilates','spinning','hiit','crossfit','zumba','boxing','swimming','strength','cardio','other') NOT NULL DEFAULT 'other', + `trainer_id` INT UNSIGNED NULL, + `duration_minutes` INT UNSIGNED NOT NULL DEFAULT 60, + `max_capacity` INT UNSIGNED NULL, + `difficulty` ENUM('beginner','intermediate','advanced','all_levels') NOT NULL DEFAULT 'all_levels', + `room` VARCHAR(100) NULL, + `status` ENUM('active','inactive','archived') NOT NULL DEFAULT 'active', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_trainer` (`trainer_id`), + KEY `idx_class_type` (`class_type`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_class_schedules` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `class_id` INT UNSIGNED NOT NULL, + `trainer_id` INT UNSIGNED NULL, + `day_of_week` ENUM('monday','tuesday','wednesday','thursday','friday','saturday','sunday') NOT NULL, + `start_time` TIME NOT NULL, + `end_time` TIME NOT NULL, + `room` VARCHAR(100) NULL, + `effective_from` DATE NULL, + `effective_until` DATE NULL, + `status` ENUM('active','cancelled','suspended') NOT NULL DEFAULT 'active', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_class` (`class_id`), + KEY `idx_trainer` (`trainer_id`), + KEY `idx_day` (`day_of_week`), + KEY `idx_status` (`status`), + CONSTRAINT `fk_schedule_class` FOREIGN KEY (`class_id`) REFERENCES `#__mokosuitegym_classes`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_class_enrollments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `class_schedule_id` INT UNSIGNED NOT NULL, + `member_id` INT UNSIGNED NOT NULL, + `enrollment_date` DATE NOT NULL, + `session_date` DATE NOT NULL, + `status` ENUM('enrolled','attended','no_show','cancelled') NOT NULL DEFAULT 'enrolled', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_schedule` (`class_schedule_id`), + KEY `idx_member` (`member_id`), + KEY `idx_session_date` (`session_date`), + KEY `idx_status` (`status`), + CONSTRAINT `fk_enrollment_schedule` FOREIGN KEY (`class_schedule_id`) REFERENCES `#__mokosuitegym_class_schedules`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_enrollment_member` FOREIGN KEY (`member_id`) REFERENCES `#__mokosuitegym_members`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_equipment` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `category` ENUM('cardio','strength','free_weights','machines','accessories','other') NOT NULL DEFAULT 'other', + `brand` VARCHAR(255) NULL, + `model` VARCHAR(255) NULL, + `serial_number` VARCHAR(255) NULL, + `purchase_date` DATE NULL, + `purchase_cost` DECIMAL(10,2) NULL, + `warranty_expiry` DATE NULL, + `location` VARCHAR(100) NULL, + `last_maintenance` DATE NULL, + `next_maintenance` DATE NULL, + `status` ENUM('operational','maintenance','out_of_order','retired') NOT NULL DEFAULT 'operational', + `notes` TEXT NULL, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `modified` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_category` (`category`), + KEY `idx_status` (`status`), + KEY `idx_next_maintenance` (`next_maintenance`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `#__mokosuitegym_check_ins` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `member_id` INT UNSIGNED NOT NULL, + `check_in_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `check_out_time` DATETIME NULL, + `location` VARCHAR(100) NULL COMMENT 'e.g. main entrance, pool, spa', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_member` (`member_id`), + KEY `idx_check_in_time` (`check_in_time`), + CONSTRAINT `fk_checkin_member` FOREIGN KEY (`member_id`) REFERENCES `#__mokosuitegym_members`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/source/packages/plg_system_mokosuitegym/sql/uninstall.mysql.sql b/source/packages/plg_system_mokosuitegym/sql/uninstall.mysql.sql new file mode 100644 index 0000000..1745298 --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/sql/uninstall.mysql.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS `#__mokosuitegym_check_ins`; +DROP TABLE IF EXISTS `#__mokosuitegym_equipment`; +DROP TABLE IF EXISTS `#__mokosuitegym_class_enrollments`; +DROP TABLE IF EXISTS `#__mokosuitegym_class_schedules`; +DROP TABLE IF EXISTS `#__mokosuitegym_classes`; +DROP TABLE IF EXISTS `#__mokosuitegym_trainers`; +DROP TABLE IF EXISTS `#__mokosuitegym_member_memberships`; +DROP TABLE IF EXISTS `#__mokosuitegym_members`; +DROP TABLE IF EXISTS `#__mokosuitegym_memberships`; diff --git a/source/packages/plg_system_mokosuitegym/src/Extension/Gym.php b/source/packages/plg_system_mokosuitegym/src/Extension/Gym.php new file mode 100644 index 0000000..dc8f148 --- /dev/null +++ b/source/packages/plg_system_mokosuitegym/src/Extension/Gym.php @@ -0,0 +1,20 @@ + + + MokoSuiteGym + mokosuitegym + 0.0.0 + 2026-06 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + (C) 2026 Moko Consulting + GPL-3.0-or-later + Layer 2 — Fitness center management, memberships, class scheduling, trainer management, equipment tracking + + plg_system_mokosuitegym.zip + com_mokosuitegym.zip + plg_webservices_mokosuitegym.zip + + + https://git.mokoconsulting.tech/api/packages/MokoConsulting/generic/updates/mokosuitegym/updates.xml + +