37 Commits

Author SHA1 Message Date
jmiller 121de98f5c feat: add scriptfile and dlid to package manifest 2026-06-28 18:57:47 +00:00
jmiller 7333892881 feat: add license key warning on install/update 2026-06-28 18:57:39 +00:00
Moko Consulting 1c5cd4007f feat: initial scaffold 2026-06-27 15:35:18 -05:00
jmiller 8bd3650fe5 feat: add webservices sys language ini 2026-06-27 20:25:48 +00:00
jmiller c6844ab101 feat: add webservices language ini 2026-06-27 20:25:45 +00:00
jmiller fd7de03582 feat: add webservices extension class 2026-06-27 20:25:43 +00:00
jmiller 38274a9f08 feat: add webservices service provider 2026-06-27 20:25:37 +00:00
jmiller a4be802d31 feat: add webservices plugin manifest 2026-06-27 20:25:32 +00:00
jmiller 1b6443aa2b feat: add component sys language ini 2026-06-27 20:25:04 +00:00
jmiller a3a73f708c feat: add component language ini 2026-06-27 20:25:01 +00:00
jmiller 118e82e2bd feat: add component config.xml 2026-06-27 20:24:57 +00:00
jmiller feb1bc0135 feat: add component access.xml 2026-06-27 20:24:54 +00:00
jmiller 5bb89ac946 feat: add bookingwaitlist template 2026-06-27 20:24:51 +00:00
jmiller 6f1a52f0ca feat: add bookinglocations template 2026-06-27 20:24:48 +00:00
jmiller df5661f366 feat: add bookingschedules template 2026-06-27 20:24:46 +00:00
jmiller bba92ebc27 feat: add bookingstaff template 2026-06-27 20:24:43 +00:00
jmiller 913f557c4e feat: add bookingservices template 2026-06-27 20:24:35 +00:00
jmiller cec6cfdfa0 feat: add bookingbookings template 2026-06-27 20:24:32 +00:00
jmiller bf6fee62f0 feat: add bookingdashboard template 2026-06-27 20:24:30 +00:00
jmiller 14a6ccd796 feat: add BookingWaitlist HtmlView 2026-06-27 20:24:28 +00:00
jmiller 942cd0e98b feat: add BookingLocations HtmlView 2026-06-27 20:24:25 +00:00
jmiller 51520f3a92 feat: add BookingSchedules HtmlView 2026-06-27 20:24:22 +00:00
jmiller 3f885d8492 feat: add BookingStaff HtmlView 2026-06-27 20:24:20 +00:00
jmiller 25e73e100c feat: add BookingServices HtmlView 2026-06-27 20:24:17 +00:00
jmiller 41f48a8559 feat: add BookingBookings HtmlView 2026-06-27 20:21:52 +00:00
jmiller c0b43c4eab feat: add BookingDashboard HtmlView 2026-06-27 20:21:50 +00:00
jmiller 0e2ad74d02 feat: add DisplayController 2026-06-27 20:21:48 +00:00
jmiller 3c59e6f2da feat: add component service provider 2026-06-27 20:21:45 +00:00
jmiller 0dc03eacc7 feat: add component manifest with submenu 2026-06-27 20:21:39 +00:00
jmiller 18f139a4a8 feat: add system plugin sys language file 2026-06-27 20:21:31 +00:00
jmiller b59c6f6cc4 feat: add system plugin language file 2026-06-27 20:21:28 +00:00
jmiller 9f0947abae feat: add uninstall SQL 2026-06-27 20:21:20 +00:00
jmiller a8fe17bd4d feat: add install SQL with 8 booking tables 2026-06-27 20:20:25 +00:00
jmiller 2aa7b638e9 feat: add system plugin extension class 2026-06-27 20:20:07 +00:00
jmiller 22eb21deb0 feat: add system plugin service provider 2026-06-27 20:20:04 +00:00
jmiller d44190450c feat: add system plugin manifest with config fieldsets 2026-06-27 20:19:58 +00:00
jmiller 1210137d73 feat: complete scaffold with system plugin, component, and webservices plugin 2026-06-27 15:03:01 -05:00
37 changed files with 961 additions and 1 deletions
+7
View File
@@ -0,0 +1,7 @@
.claude/
.mcp.json
TODO.md
*.min.css
*.min.js
vendor/
node_modules/
+21
View File
@@ -0,0 +1,21 @@
<!--
SPDX-License-Identifier: GPL-3.0-or-later
Authored-by: Moko Consulting
-->
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
### Added
- **Repository** -- initial scaffold for MokoSuiteBooking
- **System Plugin** (`plg_system_mokosuitebooking`) -- configuration and schema management
- **SQL Schema** -- services, staff, staff_services, schedules, bookings, locations, waitlist, booking_history tables
- **Admin Component** (`com_mokosuitebooking`) -- Dashboard, Services, Staff, Schedules, Bookings, Locations, Waitlist views
- **Webservices Plugin** (`plg_webservices_mokosuitebooking`) -- REST API route stubs
- **Configuration** -- basic, booking, notifications, display fieldsets
+31
View File
@@ -0,0 +1,31 @@
# MokoSuiteBooking
Appointment and resource booking for service businesses
## Quick Reference
| Field | Value |
|---|---|
| **Package** | `pkg_mokosuitebooking` |
| **Layer** | 2 (requires: Client, CRM) |
| **Language** | PHP 8.3+ |
| **Branch** | develop on `dev`, merge to `main` (protected) |
| **Wiki** | [MokoSuiteBooking Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBooking/wiki) |
## Architecture
Joomla **package** -- Layer 2 add-on. Manages services, staff, schedules, bookings, locations, and waitlist.
## 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
+30 -1
View File
@@ -1,3 +1,32 @@
# MokoSuiteBooking
MokoSuite Booking — appointment and resource booking for service businesses on Joomla 6
Appointment and resource booking for service businesses for Joomla 6.
## Overview
MokoSuiteBooking provides appointment scheduling, staff management, calendar schedules, booking lifecycle, multi-location support, and waitlist functionality as a Layer 2 MokoSuite extension.
## Requirements
- Joomla 6+
- PHP 8.3+
- MokoSuiteClient (Layer 1)
- MokoSuiteCRM (Layer 1)
## Installation
Install via the Joomla extension manager. The package includes:
- **System Plugin** (`plg_system_mokosuitebooking`) -- database schema and configuration
- **Component** (`com_mokosuitebooking`) -- admin interface
- **Webservices Plugin** (`plg_webservices_mokosuitebooking`) -- REST API routes
## License
GPL-3.0-or-later
## Links
- [Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBooking/wiki)
- [Issues](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBooking/issues)
- [MokoSuite](https://mokoconsulting.tech)
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<access component="com_mokosuitebooking">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" />
<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="booking.manage.services" title="COM_MOKOSUITEBOOKING_ACTION_MANAGE_SERVICES" />
<action name="booking.manage.staff" title="COM_MOKOSUITEBOOKING_ACTION_MANAGE_STAFF" />
<action name="booking.manage.bookings" title="COM_MOKOSUITEBOOKING_ACTION_MANAGE_BOOKINGS" />
<action name="booking.view.waitlist" title="COM_MOKOSUITEBOOKING_ACTION_VIEW_WAITLIST" />
</section>
</access>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset name="component"
label="COM_MOKOSUITEBOOKING"
description="COM_MOKOSUITEBOOKING_CONFIG_DESCRIPTION">
</fieldset>
</config>
@@ -0,0 +1,14 @@
COM_MOKOSUITEBOOKING="MokoSuiteBooking"
COM_MOKOSUITEBOOKING_DESCRIPTION="Appointment and resource booking for service businesses"
COM_MOKOSUITEBOOKING_CONFIG_DESCRIPTION="Configuration is managed in the system plugin."
COM_MOKOSUITEBOOKING_MENU_DASHBOARD="Dashboard"
COM_MOKOSUITEBOOKING_MENU_SERVICES="Services"
COM_MOKOSUITEBOOKING_MENU_STAFF="Staff"
COM_MOKOSUITEBOOKING_MENU_SCHEDULES="Schedules"
COM_MOKOSUITEBOOKING_MENU_BOOKINGS="Bookings"
COM_MOKOSUITEBOOKING_MENU_LOCATIONS="Locations"
COM_MOKOSUITEBOOKING_MENU_WAITLIST="Waitlist"
COM_MOKOSUITEBOOKING_ACTION_MANAGE_SERVICES="Manage Services"
COM_MOKOSUITEBOOKING_ACTION_MANAGE_STAFF="Manage Staff"
COM_MOKOSUITEBOOKING_ACTION_MANAGE_BOOKINGS="Manage Bookings"
COM_MOKOSUITEBOOKING_ACTION_VIEW_WAITLIST="View Waitlist"
@@ -0,0 +1,9 @@
COM_MOKOSUITEBOOKING="MokoSuiteBooking"
COM_MOKOSUITEBOOKING_DESCRIPTION="Appointment and resource booking for service businesses"
COM_MOKOSUITEBOOKING_MENU_DASHBOARD="Dashboard"
COM_MOKOSUITEBOOKING_MENU_SERVICES="Services"
COM_MOKOSUITEBOOKING_MENU_STAFF="Staff"
COM_MOKOSUITEBOOKING_MENU_SCHEDULES="Schedules"
COM_MOKOSUITEBOOKING_MENU_BOOKINGS="Bookings"
COM_MOKOSUITEBOOKING_MENU_LOCATIONS="Locations"
COM_MOKOSUITEBOOKING_MENU_WAITLIST="Waitlist"
@@ -0,0 +1,34 @@
<?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\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\MVCComponent;
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) {
$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
$component->setRouterFactory($container->get(RouterFactoryInterface::class));
return $component;
}
);
}
};
@@ -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\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
class DisplayController extends BaseController
{
protected $default_view = 'bookingdashboard';
}
@@ -0,0 +1,24 @@
<?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\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('Dashboard', 'home');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Bookings;
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('Bookings', 'book');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Locations;
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('Locations', 'location');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Schedules;
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('Schedules', 'calendar');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Services;
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('Services', 'list');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Staff;
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('Staff', 'users');
parent::display($tpl);
}
}
@@ -0,0 +1,24 @@
<?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\View\Waitlist;
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('Waitlist', 'clock');
parent::display($tpl);
}
}
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Dashboard</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Bookings</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Locations</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Schedules</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Services</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Staff</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,15 @@
<?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;
?>
<div class="container">
<h2>Waitlist</h2>
<p>Coming soon.</p>
</div>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>com_mokosuitebooking</name>
<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</copyright>
<license>GPL-3.0-or-later</license>
<description>COM_MOKOSUITEBOOKING_DESCRIPTION</description>
<namespace path="admin/src">Moko\Component\MokoSuiteBooking</namespace>
<administration>
<menu>COM_MOKOSUITEBOOKING</menu>
<submenu>
<menu link="option=com_mokosuitebooking&amp;view=bookingdashboard">COM_MOKOSUITEBOOKING_MENU_DASHBOARD</menu>
<menu link="option=com_mokosuitebooking&amp;view=services">COM_MOKOSUITEBOOKING_MENU_SERVICES</menu>
<menu link="option=com_mokosuitebooking&amp;view=staff">COM_MOKOSUITEBOOKING_MENU_STAFF</menu>
<menu link="option=com_mokosuitebooking&amp;view=schedules">COM_MOKOSUITEBOOKING_MENU_SCHEDULES</menu>
<menu link="option=com_mokosuitebooking&amp;view=bookings">COM_MOKOSUITEBOOKING_MENU_BOOKINGS</menu>
<menu link="option=com_mokosuitebooking&amp;view=locations">COM_MOKOSUITEBOOKING_MENU_LOCATIONS</menu>
<menu link="option=com_mokosuitebooking&amp;view=waitlist">COM_MOKOSUITEBOOKING_MENU_WAITLIST</menu>
</submenu>
<files folder="admin">
<folder>language</folder>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
</administration>
<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>
</extension>
@@ -0,0 +1,20 @@
PLG_SYSTEM_MOKOSUITEBOOKING="System - MokoSuiteBooking"
PLG_SYSTEM_MOKOSUITEBOOKING_DESCRIPTION="MokoSuiteBooking system plugin — schema and configuration management"
PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_BASIC="Basic"
PLG_SYSTEM_MOKOSUITEBOOKING_BUSINESS_NAME="Business Name"
PLG_SYSTEM_MOKOSUITEBOOKING_CURRENCY="Currency"
PLG_SYSTEM_MOKOSUITEBOOKING_TIMEZONE="Timezone"
PLG_SYSTEM_MOKOSUITEBOOKING_TIME_SLOT_MINUTES="Time Slot (Minutes)"
PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_BOOKING="Booking"
PLG_SYSTEM_MOKOSUITEBOOKING_MAX_ADVANCE_DAYS="Max Advance Booking (Days)"
PLG_SYSTEM_MOKOSUITEBOOKING_CANCELLATION_WINDOW="Cancellation Window (Hours)"
PLG_SYSTEM_MOKOSUITEBOOKING_AUTO_CONFIRM="Auto-Confirm"
PLG_SYSTEM_MOKOSUITEBOOKING_ALLOW_WALK_INS="Allow Walk-Ins"
PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_NOTIFICATIONS="Notifications"
PLG_SYSTEM_MOKOSUITEBOOKING_CONFIRMATION_EMAIL="Confirmation Email"
PLG_SYSTEM_MOKOSUITEBOOKING_REMINDER_HOURS="Reminder (Hours Before)"
PLG_SYSTEM_MOKOSUITEBOOKING_NOSHOW_THRESHOLD="No-Show Threshold (Minutes)"
PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_DISPLAY="Display"
PLG_SYSTEM_MOKOSUITEBOOKING_CALENDAR_START="Calendar Start Hour"
PLG_SYSTEM_MOKOSUITEBOOKING_CALENDAR_END="Calendar End Hour"
PLG_SYSTEM_MOKOSUITEBOOKING_SLOTS_PER_PAGE="Slots Per Page"
@@ -0,0 +1,2 @@
PLG_SYSTEM_MOKOSUITEBOOKING="System - MokoSuiteBooking"
PLG_SYSTEM_MOKOSUITEBOOKING_DESCRIPTION="MokoSuiteBooking system plugin — schema and configuration management"
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>plg_system_mokosuitebooking</name>
<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</copyright>
<license>GPL-3.0-or-later</license>
<description>PLG_SYSTEM_MOKOSUITEBOOKING_DESCRIPTION</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteBooking</namespace>
<files>
<folder plugin="mokosuitebooking">services</folder>
<folder>src</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="PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_BASIC">
<field name="business_name" type="text" label="PLG_SYSTEM_MOKOSUITEBOOKING_BUSINESS_NAME" default="" />
<field name="currency" type="text" label="PLG_SYSTEM_MOKOSUITEBOOKING_CURRENCY" default="USD" />
<field name="timezone" type="timezone" label="PLG_SYSTEM_MOKOSUITEBOOKING_TIMEZONE" default="America/New_York" />
<field name="time_slot_minutes" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_TIME_SLOT_MINUTES" default="30" min="5" max="120" step="5" />
</fieldset>
<fieldset name="booking" label="PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_BOOKING">
<field name="max_advance_days" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_MAX_ADVANCE_DAYS" default="90" min="1" max="365" />
<field name="cancellation_window_hours" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_CANCELLATION_WINDOW" default="24" min="0" max="168" />
<field name="auto_confirm" type="radio" label="PLG_SYSTEM_MOKOSUITEBOOKING_AUTO_CONFIRM" default="0" class="btn-group"><option value="0">JNO</option><option value="1">JYES</option></field>
<field name="allow_walk_ins" type="radio" label="PLG_SYSTEM_MOKOSUITEBOOKING_ALLOW_WALK_INS" default="1" class="btn-group"><option value="0">JNO</option><option value="1">JYES</option></field>
</fieldset>
<fieldset name="notifications" label="PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_NOTIFICATIONS">
<field name="confirmation_email" type="radio" label="PLG_SYSTEM_MOKOSUITEBOOKING_CONFIRMATION_EMAIL" default="1" class="btn-group"><option value="0">JNO</option><option value="1">JYES</option></field>
<field name="reminder_hours_before" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_REMINDER_HOURS" default="24" min="0" max="72" />
<field name="noshow_threshold_minutes" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_NOSHOW_THRESHOLD" default="15" min="5" max="60" />
</fieldset>
<fieldset name="display" label="PLG_SYSTEM_MOKOSUITEBOOKING_FIELDSET_DISPLAY">
<field name="calendar_start_hour" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_CALENDAR_START" default="8" min="0" max="12" />
<field name="calendar_end_hour" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_CALENDAR_END" default="18" min="12" max="23" />
<field name="slots_per_page" type="number" label="PLG_SYSTEM_MOKOSUITEBOOKING_SLOTS_PER_PAGE" default="20" min="5" max="50" />
</fieldset>
</fields>
</config>
</extension>
@@ -0,0 +1,33 @@
<?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,150 @@
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 60,
`buffer_minutes` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
`price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`max_participants` TINYINT 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,
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,
`name` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL DEFAULT '',
`phone` VARCHAR(50) NOT NULL DEFAULT '',
`title` VARCHAR(100) NOT NULL DEFAULT '',
`bio` TEXT,
`photo` VARCHAR(500) NOT NULL DEFAULT '',
`color` VARCHAR(7) NOT NULL DEFAULT '#3788d8',
`max_daily_bookings` TINYINT UNSIGNED NOT NULL DEFAULT 20,
`rating` DECIMAL(3,2) NOT NULL DEFAULT 5.00,
`total_bookings` INT UNSIGNED NOT NULL DEFAULT 0,
`published` TINYINT NOT NULL DEFAULT 1,
`ordering` INT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_contact` (`contact_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_staff_services` (
`staff_id` INT UNSIGNED NOT NULL,
`service_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`staff_id`, `service_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,
`is_recurring` TINYINT NOT NULL DEFAULT 1,
`specific_date` DATE DEFAULT NULL,
`override_type` ENUM('available','unavailable') DEFAULT NULL,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_staff` (`staff_id`),
KEY `idx_location` (`location_id`),
KEY `idx_day` (`day_of_week`),
KEY `idx_date` (`specific_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,
`service_id` INT UNSIGNED NOT NULL,
`staff_id` INT UNSIGNED DEFAULT NULL,
`location_id` INT UNSIGNED DEFAULT NULL,
`customer_contact_id` INT DEFAULT NULL,
`customer_name` VARCHAR(255) NOT NULL,
`customer_email` VARCHAR(255) NOT NULL DEFAULT '',
`customer_phone` VARCHAR(50) NOT NULL DEFAULT '',
`status` ENUM('pending','confirmed','checked_in','in_progress','completed','cancelled','no_show') NOT NULL DEFAULT 'pending',
`booking_date` DATE NOT NULL,
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`participants` TINYINT UNSIGNED NOT NULL DEFAULT 1,
`price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`deposit_paid` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`payment_status` ENUM('pending','deposit_paid','paid','refunded') NOT NULL DEFAULT 'pending',
`source` ENUM('admin','online','phone','walk_in') NOT NULL DEFAULT 'online',
`notes` TEXT,
`customer_notes` TEXT,
`reminder_sent` TINYINT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
`created_by` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_ref` (`booking_ref`),
KEY `idx_service` (`service_id`),
KEY `idx_staff` (`staff_id`),
KEY `idx_customer` (`customer_contact_id`),
KEY `idx_status` (`status`),
KEY `idx_date` (`booking_date`),
KEY `idx_datetime` (`booking_date`, `start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
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 '',
`phone` VARCHAR(50) NOT NULL DEFAULT '',
`email` VARCHAR(255) NOT NULL DEFAULT '',
`timezone` VARCHAR(50) NOT NULL DEFAULT 'America/New_York',
`published` TINYINT NOT NULL DEFAULT 1,
`ordering` INT NOT NULL DEFAULT 0,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_waitlist` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`service_id` INT UNSIGNED NOT NULL,
`staff_id` INT UNSIGNED DEFAULT NULL,
`location_id` INT UNSIGNED DEFAULT NULL,
`customer_contact_id` INT DEFAULT NULL,
`customer_name` VARCHAR(255) NOT NULL,
`customer_phone` VARCHAR(50) NOT NULL DEFAULT '',
`customer_email` VARCHAR(255) NOT NULL DEFAULT '',
`preferred_date` DATE DEFAULT NULL,
`preferred_time` TIME DEFAULT NULL,
`status` ENUM('waiting','offered','booked','expired','cancelled') NOT NULL DEFAULT 'waiting',
`position` INT UNSIGNED NOT NULL DEFAULT 0,
`notes` TEXT,
`created` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_service` (`service_id`),
KEY `idx_customer` (`customer_contact_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `#__mokosuitebooking_booking_history` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`booking_id` INT UNSIGNED NOT NULL,
`action` ENUM('created','confirmed','rescheduled','cancelled','completed','no_show','reminder_sent') NOT NULL,
`old_date` DATE DEFAULT NULL,
`old_time` TIME DEFAULT NULL,
`new_date` DATE DEFAULT NULL,
`new_time` TIME DEFAULT NULL,
`note` TEXT,
`created` DATETIME NOT NULL,
`created_by` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_booking` (`booking_id`),
KEY `idx_action` (`action`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -0,0 +1,8 @@
DROP TABLE IF EXISTS `#__mokosuitebooking_booking_history`;
DROP TABLE IF EXISTS `#__mokosuitebooking_waitlist`;
DROP TABLE IF EXISTS `#__mokosuitebooking_locations`;
DROP TABLE IF EXISTS `#__mokosuitebooking_bookings`;
DROP TABLE IF EXISTS `#__mokosuitebooking_schedules`;
DROP TABLE IF EXISTS `#__mokosuitebooking_staff_services`;
DROP TABLE IF EXISTS `#__mokosuitebooking_staff`;
DROP TABLE IF EXISTS `#__mokosuitebooking_services`;
@@ -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;
final class Booking extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [];
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="webservices" method="upgrade">
<name>plg_webservices_mokosuitebooking</name>
<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</copyright>
<license>GPL-3.0-or-later</license>
<description>PLG_WEBSERVICES_MOKOSUITEBOOKING_DESCRIPTION</description>
<namespace path="src">Moko\Plugin\Webservices\MokoSuiteBooking</namespace>
<files>
<folder plugin="mokosuitebooking">services</folder>
<folder>src</folder>
</files>
</extension>
@@ -0,0 +1,33 @@
<?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);
$plugin = new MokoSuiteBooking($dispatcher, (array) PluginHelper::getPlugin('webservices', 'mokosuitebooking'));
$plugin->setApplication(\Joomla\CMS\Factory::getApplication());
return $plugin;
}
);
}
};
@@ -0,0 +1,30 @@
<?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;
final class MokoSuiteBooking extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onBeforeApiRoute' => 'onBeforeApiRoute',
];
}
public function onBeforeApiRoute(&$router): void
{
// Route registrations will be added during development
}
}
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="package" method="upgrade">
<name>pkg_mokosuitebooking</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</copyright>
<license>GPL-3.0-or-later</license>
<description>Appointment and resource booking for service businesses</description>
<php_minimum>8.3</php_minimum>
<dlid prefix="dlid=" suffix=""/>
<blockChildUninstall>true</blockChildUninstall>
<scriptfile>script.php</scriptfile>
<files>
<file type="plugin" id="mokosuitebooking" group="system">packages/plg_system_mokosuitebooking</file>
<file type="component" id="com_mokosuitebooking">packages/com_mokosuitebooking</file>
<file type="plugin" id="mokosuitebooking" group="webservices">packages/plg_webservices_mokosuitebooking</file>
</files>
<updateservers>
<server type="extension" name="MokoSuiteBooking Updates">https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteBooking/updates.xml</server>
</updateservers>
</extension>
+74
View File
@@ -0,0 +1,74 @@
<?php
/**
* @package MokoSuiteBooking
* @subpackage pkg_mokosuitebooking
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Installer\InstallerAdapter;
use Joomla\CMS\Log\Log;
/**
* Package installation script for MokoSuiteBooking.
*/
class Pkg_MokoSuiteBookingInstallerScript
{
public function postflight(string $type, InstallerAdapter $adapter): void
{
$this->warnMissingLicenseKey();
}
private function warnMissingLicenseKey(): void
{
try
{
$db = Factory::getDbo();
$app = Factory::getApplication();
$query = $db->getQuery(true)
->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')])
->from($db->quoteName('#__update_sites'))
->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuiteBooking%')
. ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoSuiteBooking%') . ')')
->setLimit(1);
$db->setQuery($query);
$site = $db->loadObject();
if ($site)
{
$extraQuery = (string) ($site->extra_query ?? '');
if (!empty($extraQuery) && strpos($extraQuery, 'dlid=') !== false)
{
parse_str($extraQuery, $parsed);
if (!empty($parsed['dlid']))
{
return;
}
}
$editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id;
}
else
{
$editUrl = 'index.php?option=com_installer&view=updatesites';
}
$app->enqueueMessage(
'<strong>Moko Consulting License Key Required</strong> — '
. 'No download key is configured. Updates will not be available until a valid license key is entered. '
. '<a href="' . $editUrl . '" class="btn btn-sm btn-warning ms-2">Enter License Key</a>',
'warning'
);
}
catch (\Throwable $e)
{
// Silent — avoid breaking install if update_sites query fails
}
}
}