feat: complete scaffold with system plugin, component, and webservices plugin

This commit is contained in:
2026-06-27 15:03:01 -05:00
parent 2208a9e3e8
commit 1210137d73
35 changed files with 837 additions and 2 deletions
+7
View File
@@ -0,0 +1,7 @@
.claude/
.mcp.json
TODO.md
*.min.css
*.min.js
vendor/
node_modules/
+19
View File
@@ -0,0 +1,19 @@
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
INGROUP: MokoSuitebooking.Documentation
BRIEF: Version history using Keep a Changelog
-->
# Changelog
## [Unreleased]
### Added
- **Repository** -- initial scaffold
- **System Plugin** -- Extension class, service provider
- **SQL Schema** -- 8 tables: locations, services, staff, schedules, availability_overrides, bookings, booking_services, waitlist
- **Admin Component** -- 6 views: dashboard, bookings, services, staff, schedules, locations
- **Webservices Plugin** -- 7 API routes
- **Configuration** -- plugin settings across 4 fieldsets
- **Access Control** -- granular permissions
+30
View File
@@ -0,0 +1,30 @@
# MokoSuitebooking
Appointment and resource booking for service businesses for Joomla 6.
## Quick Reference
| Field | Value |
|---|---|
| **Package** | `pkg_mokosuitebooking` |
| **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 clients, service catalog with staff scheduling and online booking.
## 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
+44 -2
View File
@@ -1,3 +1,45 @@
# MokoSuiteBooking
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
-->
MokoSuite Booking — appointment and resource booking for service businesses on Joomla 6
# MokoSuite booking
Appointment and resource booking for service businesses for MokoSuite on Joomla 6.
## Overview
MokoSuitebooking is a **Layer 2** extension in the MokoSuite platform, building on MokoSuiteClient (Layer 0) and MokoSuiteCRM (Layer 1).
## Features
- **Service Catalog** -- services with duration, pricing, staff assignment
- **Online Booking** -- real-time availability with booking widget
- **Staff Scheduling** -- recurring availability patterns with overrides
- **Multi-Location** -- support for multiple business locations
- **Walk-In Queue** -- walk-in queue management
- **Client History** -- CRM contact-linked visit frequency and spend tracking
- **Waitlist** -- automated notification when slots open
- **REST API** -- full CRUD for services, staff, bookings, schedules, locations
- **Access Control** -- granular permissions
## Requirements
- Joomla 6.x
- PHP 8.3+
- MokoSuiteClient (Layer 0)
- MokoSuiteCRM (Layer 1)
## Installation
Install via Joomla Extension Manager using the package file `pkg_mokosuitebooking.zip`.
## License
GNU General Public License v3.0 or later.
## Links
- [Documentation](https://git.mokoconsulting.tech/MokoConsulting/MokoSuitebooking/wiki)
- [Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoSuitebooking/issues)
- [MokoSuite Platform](https://mokoconsulting.tech)
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<access component="com_mokosuitebooking">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" />
<action name="core.options" title="JACTION_OPTIONS" />
<action name="core.manage" title="JACTION_MANAGE" />
<action name="core.create" title="JACTION_CREATE" />
<action name="core.delete" title="JACTION_DELETE" />
<action name="core.edit" title="JACTION_EDIT" />
<action name="core.edit.state" title="JACTION_EDITSTATE" />
<action name="core.edit.own" title="JACTION_EDITOWN" />
<action name="booking.manage.services" title="Manage Services" />
<action name="booking.manage.staff" title="Manage Staff" />
<action name="booking.manage.schedules" title="Manage Schedules" />
<action name="booking.view.reports" title="View Reports" />
</section>
</access>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset name="permissions" label="JCONFIG_PERMISSIONS_LABEL" description="JCONFIG_PERMISSIONS_DESC">
<field name="rules" type="rules" label="JCONFIG_PERMISSIONS_LABEL" validate="rules" filter="rules" component="com_mokosuitebooking" section="component" />
</fieldset>
</config>
@@ -0,0 +1,9 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
COM_MOKOSUITEBOOKING="MokoSuite Booking"
COM_MOKOSUITEBOOKING_DASHBOARD="Dashboard"
COM_MOKOSUITEBOOKING_BOOKINGS="Bookings"
COM_MOKOSUITEBOOKING_SERVICES="Services"
COM_MOKOSUITEBOOKING_STAFF="Staff"
COM_MOKOSUITEBOOKING_SCHEDULES="Schedules"
COM_MOKOSUITEBOOKING_LOCATIONS="Locations"
@@ -0,0 +1,4 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
COM_MOKOSUITEBOOKING="MokoSuite Booking"
COM_MOKOSUITEBOOKING_XML_DESCRIPTION="Appointment and resource booking"
@@ -0,0 +1,28 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
defined('_JEXEC') or die;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
return new class implements ServiceProviderInterface {
public function register(Container $container): void {
$container->set(ComponentInterface::class, function (Container $container) {
$c = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
$c->setMVCFactory($container->get(MVCFactoryInterface::class));
$c->setRouterFactory($container->get(RouterFactoryInterface::class));
return $c;
});
}
};
@@ -0,0 +1,18 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'bookingdashboard';
}
@@ -0,0 +1,17 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\Model;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
class BookingDashboardModel extends BaseDatabaseModel
{
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingBookings;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Bookings');
parent::display($tpl);
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingDashboard;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Dashboard');
parent::display($tpl);
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingLocations;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Locations');
parent::display($tpl);
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingSchedules;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Schedules');
parent::display($tpl);
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingServices;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Services');
parent::display($tpl);
}
}
@@ -0,0 +1,23 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Component\MokoSuiteBooking\Administrator\View\BookingStaff;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
class HtmlView extends BaseHtmlView
{
public function display($tpl = null): void
{
ToolbarHelper::title('MokoSuite Booking - Staff');
parent::display($tpl);
}
}
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Bookings</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Dashboard</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Locations</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Schedules</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Services</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
defined('_JEXEC') or die;
?>
<div class="container">
<h2>Staff</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>MokoSuite Booking</name>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<creationDate>2026-06-27</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting.</copyright>
<license>GPL-3.0-or-later</license>
<version>06.00.00</version>
<description>Appointment and resource booking</description>
<namespace path="src">Moko\Component\MokoSuiteBooking</namespace>
<administration>
<files folder="admin">
<filename>access.xml</filename>
<filename>config.xml</filename>
<folder>language</folder>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages folder="admin/language">
<language tag="en-GB">en-GB/com_mokosuitebooking.ini</language>
<language tag="en-GB">en-GB/com_mokosuitebooking.sys.ini</language>
</languages>
<menu>COM_MOKOSUITEBOOKING</menu>
<submenu>
<menu link="option=com_mokosuitebooking&amp;view=bookingdashboard">COM_MOKOSUITEBOOKING_DASHBOARD</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookingbookings">COM_MOKOSUITEBOOKING_BOOKINGS</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookingservices">COM_MOKOSUITEBOOKING_SERVICES</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookingstaff">COM_MOKOSUITEBOOKING_STAFF</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookingschedules">COM_MOKOSUITEBOOKING_SCHEDULES</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookinglocations">COM_MOKOSUITEBOOKING_LOCATIONS</menu>
</submenu>
</administration>
</extension>
@@ -0,0 +1,4 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
PLG_SYSTEM_MOKOSUITEBOOKING="System - MokoSuite Booking"
PLG_SYSTEM_MOKOSUITEBOOKING_DESC="Appointment booking helpers and schema"
@@ -0,0 +1,4 @@
; Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
; SPDX-License-Identifier: GPL-3.0-or-later
PLG_SYSTEM_MOKOSUITEBOOKING="System - MokoSuite Booking"
PLG_SYSTEM_MOKOSUITEBOOKING_DESC="Appointment booking helpers and schema"
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuite Booking</name>
<element>mokosuitebooking</element>
<author>Moko Consulting</author>
<creationDate>2026-06-27</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<version>06.00.00</version>
<php_minimum>8.3</php_minimum>
<description>PLG_SYSTEM_MOKOSUITEBOOKING_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteBooking</namespace>
<files>
<folder>src</folder>
<folder>services</folder>
<folder>language</folder>
<folder>sql</folder>
</files>
<languages folder="language">
<language tag="en-GB">en-GB/plg_system_mokosuitebooking.ini</language>
<language tag="en-GB">en-GB/plg_system_mokosuitebooking.sys.ini</language>
</languages>
<install><sql><file driver="mysql" charset="utf8">sql/install.mysql.sql</file></sql></install>
<uninstall><sql><file driver="mysql" charset="utf8">sql/uninstall.mysql.sql</file></sql></uninstall>
<config>
<fields name="params">
<fieldset name="basic" label="Booking Defaults">
<field name="timezone" type="timezone" default="America/New_York" label="Default Timezone" />
<field name="slot_duration" type="number" default="30" label="Default Slot Duration (min)" />
<field name="default_currency" type="text" default="USD" label="Default Currency" />
</fieldset>
<fieldset name="booking" label="Booking Rules">
<field name="advance_booking_days" type="number" default="30" label="Max Advance Booking (days)" />
<field name="cancellation_window_hours" type="number" default="24" label="Cancellation Window (hours)" />
<field name="auto_confirm" type="radio" default="1" label="Auto-Confirm Bookings" class="btn-group btn-group-yesno">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
</fieldset>
<fieldset name="notifications" label="Notifications">
<field name="reminder_hours" type="number" default="24" label="Reminder Before (hours)" />
<field name="no_show_policy" type="list" default="none" label="No-Show Policy">
<option value="none">None</option>
<option value="flag">Flag Only</option>
<option value="fee">Apply Fee</option>
</field>
</fieldset>
<fieldset name="display" label="Display">
<field name="calendar_default_view" type="list" default="week" label="Default Calendar View">
<option value="day">Day</option>
<option value="week">Week</option>
<option value="month">Month</option>
</field>
</fieldset>
</fields>
</config>
</extension>
@@ -0,0 +1,27 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Moko\Plugin\System\MokoSuiteBooking\Extension\Booking;
return new class implements ServiceProviderInterface {
public function register(Container $container): void {
$container->set(PluginInterface::class, function (Container $container) {
$dispatcher = $container->get(DispatcherInterface::class);
$plugin = new Booking($dispatcher, (array) PluginHelper::getPlugin('system', 'mokosuitebooking'));
$plugin->setApplication(\Joomla\CMS\Factory::getApplication());
return $plugin;
});
}
};
@@ -0,0 +1,163 @@
--
-- MokoSuite Booking Tables
--
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_locations` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`address` VARCHAR(500) NOT NULL DEFAULT '',
`city` VARCHAR(100) NOT NULL DEFAULT '',
`state` VARCHAR(100) NOT NULL DEFAULT '',
`postal_code` VARCHAR(20) NOT NULL DEFAULT '',
`country` VARCHAR(100) NOT NULL DEFAULT '',
`timezone` VARCHAR(50) NOT NULL DEFAULT 'America/New_York',
`phone` VARCHAR(50) NOT NULL DEFAULT '',
`email` VARCHAR(255) NOT NULL DEFAULT '',
`photo` VARCHAR(500) NOT NULL DEFAULT '',
`notes` TEXT,
`published` TINYINT NOT NULL DEFAULT 1,
`ordering` INT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
`created_by` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_services` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`description` TEXT,
`category` VARCHAR(100) NOT NULL DEFAULT '',
`duration_minutes` SMALLINT UNSIGNED NOT NULL DEFAULT 30,
`buffer_minutes` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
`price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`currency` VARCHAR(3) NOT NULL DEFAULT 'USD',
`max_attendees` SMALLINT UNSIGNED NOT NULL DEFAULT 1,
`requires_deposit` TINYINT NOT NULL DEFAULT 0,
`deposit_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`color` VARCHAR(7) NOT NULL DEFAULT '#3788d8',
`photo` VARCHAR(500) NOT NULL DEFAULT '',
`published` TINYINT NOT NULL DEFAULT 1,
`ordering` INT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
`created_by` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_category` (`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_staff` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`contact_id` INT DEFAULT NULL,
`user_id` INT DEFAULT NULL,
`display_name` VARCHAR(255) NOT NULL,
`title` VARCHAR(255) NOT NULL DEFAULT '',
`email` VARCHAR(255) NOT NULL DEFAULT '',
`phone` VARCHAR(50) NOT NULL DEFAULT '',
`bio` TEXT,
`photo` VARCHAR(500) NOT NULL DEFAULT '',
`color` VARCHAR(7) NOT NULL DEFAULT '#3788d8',
`services_json` JSON DEFAULT NULL,
`locations_json` JSON DEFAULT NULL,
`published` TINYINT NOT NULL DEFAULT 1,
`ordering` INT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_contact` (`contact_id`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_schedules` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`staff_id` INT UNSIGNED NOT NULL,
`location_id` INT UNSIGNED DEFAULT NULL,
`day_of_week` TINYINT UNSIGNED NOT NULL,
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`effective_from` DATE DEFAULT NULL,
`effective_until` DATE DEFAULT NULL,
`published` TINYINT NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `idx_staff` (`staff_id`),
KEY `idx_location` (`location_id`),
KEY `idx_day` (`day_of_week`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_availability_overrides` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`staff_id` INT UNSIGNED NOT NULL,
`override_date` DATE NOT NULL,
`available` TINYINT NOT NULL DEFAULT 0,
`start_time` TIME DEFAULT NULL,
`end_time` TIME DEFAULT NULL,
`reason` VARCHAR(255) NOT NULL DEFAULT '',
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_staff_date` (`staff_id`, `override_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_bookings` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`booking_ref` VARCHAR(20) NOT NULL,
`contact_id` INT DEFAULT NULL,
`staff_id` INT UNSIGNED DEFAULT NULL,
`location_id` INT UNSIGNED DEFAULT NULL,
`customer_name` VARCHAR(255) NOT NULL,
`customer_email` VARCHAR(255) NOT NULL DEFAULT '',
`customer_phone` VARCHAR(50) NOT NULL DEFAULT '',
`booking_date` DATE NOT NULL,
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`status` ENUM('pending','confirmed','in_progress','completed','cancelled','no_show') NOT NULL DEFAULT 'pending',
`total_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`deposit_paid` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`payment_status` ENUM('unpaid','partial','paid','refunded') NOT NULL DEFAULT 'unpaid',
`notes` TEXT,
`internal_notes` TEXT,
`source` ENUM('admin','website','api','walk_in') NOT NULL DEFAULT 'admin',
`cancelled_at` DATETIME DEFAULT NULL,
`cancellation_reason` VARCHAR(500) NOT NULL DEFAULT '',
`reminder_sent` TINYINT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
`created_by` INT NOT NULL DEFAULT 0,
`modified` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ref` (`booking_ref`),
KEY `idx_contact` (`contact_id`),
KEY `idx_staff` (`staff_id`),
KEY `idx_location` (`location_id`),
KEY `idx_date` (`booking_date`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_booking_services` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`booking_id` INT UNSIGNED NOT NULL,
`service_id` INT UNSIGNED NOT NULL,
`staff_id` INT UNSIGNED DEFAULT NULL,
`price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`duration_minutes` SMALLINT UNSIGNED NOT NULL DEFAULT 30,
PRIMARY KEY (`id`),
KEY `idx_booking` (`booking_id`),
KEY `idx_service` (`service_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_waitlist` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`contact_id` INT DEFAULT NULL,
`service_id` INT UNSIGNED NOT NULL,
`staff_id` INT UNSIGNED DEFAULT NULL,
`location_id` INT UNSIGNED DEFAULT NULL,
`customer_name` VARCHAR(255) NOT NULL,
`customer_email` VARCHAR(255) NOT NULL DEFAULT '',
`customer_phone` VARCHAR(50) NOT NULL DEFAULT '',
`preferred_date_start` DATE DEFAULT NULL,
`preferred_date_end` DATE DEFAULT NULL,
`preferred_time_start` TIME DEFAULT NULL,
`preferred_time_end` TIME DEFAULT NULL,
`status` ENUM('waiting','notified','booked','expired','cancelled') NOT NULL DEFAULT 'waiting',
`notified_at` DATETIME DEFAULT NULL,
`notes` TEXT,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_service` (`service_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -0,0 +1,12 @@
--
-- MokoSuite Booking — Uninstall
--
DROP TABLE IF EXISTS `#__mokosuitebooking_waitlist`;
DROP TABLE IF EXISTS `#__mokosuitebooking_booking_services`;
DROP TABLE IF EXISTS `#__mokosuitebooking_bookings`;
DROP TABLE IF EXISTS `#__mokosuitebooking_availability_overrides`;
DROP TABLE IF EXISTS `#__mokosuitebooking_schedules`;
DROP TABLE IF EXISTS `#__mokosuitebooking_staff`;
DROP TABLE IF EXISTS `#__mokosuitebooking_services`;
DROP TABLE IF EXISTS `#__mokosuitebooking_locations`;
@@ -0,0 +1,22 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Plugin\System\MokoSuiteBooking\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
class Booking extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [];
}
}
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<extension type="plugin" group="webservices" method="upgrade">
<name>Web Services - MokoSuite Booking</name>
<element>mokosuitebooking</element>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<creationDate>2026-06-27</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting.</copyright>
<version>06.00.00</version>
<license>GPL-3.0-or-later</license>
<description>REST API routes for MokoSuite Booking operations</description>
<namespace path="src">Moko\Plugin\WebServices\MokoSuiteBooking</namespace>
<files><folder>src</folder><folder>services</folder></files>
</extension>
@@ -0,0 +1,25 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Moko\Plugin\WebServices\MokoSuiteBooking\Extension\MokoSuiteBooking;
return new class implements ServiceProviderInterface {
public function register(Container $container): void {
$container->set(PluginInterface::class, function (Container $container) {
$dispatcher = $container->get(DispatcherInterface::class);
return new MokoSuiteBooking($dispatcher, (array) PluginHelper::getPlugin('webservices', 'mokosuitebooking'));
});
}
};
@@ -0,0 +1,43 @@
<?php
/**
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
* Authored-by: Moko Consulting
*/
namespace Moko\Plugin\WebServices\MokoSuiteBooking\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\ApiRouter;
use Joomla\Event\SubscriberInterface;
class MokoSuiteBooking extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onBeforeApiRoute' => 'onBeforeApiRoute',
];
}
public function onBeforeApiRoute($event): void
{
$router = $event->getArgument('router');
$routes = [
'services', 'staff', 'bookings', 'schedules',
'locations', 'availability', 'waitlist',
];
foreach ($routes as $route) {
$router->createCRUDRoutes(
"v1/booking/{$route}",
$route,
['component' => 'com_mokosuitebooking']
);
}
}
}
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="package" method="upgrade">
<name>Package - MokoSuite Booking</name>
<packagename>mokosuitebooking</packagename>
<version>06.00.00</version>
<creationDate>2026-06-27</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl>
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
<license>GNU General Public License version 3 or later; see LICENSE</license>
<description>Appointment and resource booking for service businesses</description>
<php_minimum>8.3</php_minimum>
<dlid prefix="dlid=" suffix=""/>
<blockChildUninstall>true</blockChildUninstall>
<files folder="packages">
<file type="plugin" id="plg_system_mokosuitebooking" group="system">plg_system_mokosuitebooking.zip</file>
<file type="component" id="com_mokosuitebooking">com_mokosuitebooking.zip</file>
<file type="plugin" id="plg_webservices_mokosuitebooking" group="webservices">plg_webservices_mokosuitebooking.zip</file>
</files>
<updateservers>
<server type="extension" priority="1" name="Package - MokoSuite Booking">https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBooking/updates.xml</server>
</updateservers>
</extension>