fc70cde9d5
CI / No Debug Code (pull_request) Successful in 4s
CI / Tests (PHP 8.2) (pull_request) Successful in 52s
CI / Tests (PHP 8.1) (pull_request) Successful in 54s
CI / Tests (PHP 8.3) (pull_request) Successful in 1m29s
CI / Coding Standards (pull_request) Successful in 1m57s
CI / PHPStan (pull_request) Successful in 2m14s
CI / Build Plugin Zip (pull_request) Has been skipped
Wrap the four shortcodes (us_booking, us_student_login, us_student_register, us_group_classes) in dynamic blocks so pages can be previewed and styled in the block editor. Front-end rendering delegates to the same page objects the shortcodes use; in the editor's block-renderer REST preview a static, script-free BlockPreview is rendered instead (no live REST calls, redirects, or Stripe.js). The editor script (vanilla JS, no build step) registers each block with wp.serverSideRender previews and shortcode transforms; frontend.css is attached as the block style so previews pick up theme styling. Resolves #44 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
114 lines
6.4 KiB
Markdown
114 lines
6.4 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
composer install # Install all dependencies
|
|
|
|
composer test # Run the full test suite (required after every change)
|
|
composer lint # PHPStan static analysis
|
|
composer cs # PHPCS coding standards check
|
|
composer cs:fix # Auto-fix coding standards
|
|
|
|
# Run a single test file
|
|
./vendor/bin/phpunit tests/Unit/Availability/AvailabilityRepositoryTest.php
|
|
|
|
# Run a single test by name
|
|
./vendor/bin/phpunit --filter testInsertCallsWpdbInsertAndReturnsId
|
|
```
|
|
|
|
**Run `composer test` after every code change before considering a task complete.**
|
|
|
|
## Architecture
|
|
|
|
### Plugin Bootstrap
|
|
`unsupervised-schedular.php` defines constants (`USC_VERSION`, `USC_PLUGIN_DIR`, `USC_PLUGIN_URL`), registers activation/deactivation hooks, then calls `Plugin::boot()` on `plugins_loaded`. No logic lives in the root file.
|
|
|
|
### Directory Structure
|
|
```
|
|
src/ — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\)
|
|
Availability/ — Availability slots: value object, repository, controller, REST endpoint
|
|
Booking/ — Lessons/bookings: value object, repository, controller, REST endpoint, shortcode page
|
|
Auth/ — Roles, capabilities, login page
|
|
Plugin.php — Wires all components together on plugins_loaded
|
|
Installer.php — Creates DB tables and roles on activation
|
|
Schema.php — CREATE TABLE SQL for dbDelta
|
|
AdminMenu.php — Registers wp-admin menu pages
|
|
RestRegistrar.php — Registers all REST routes under us-scheduler/v1
|
|
ShortcodeRegistrar.php — Registers [us_booking] and [us_student_login] shortcodes
|
|
BlockRegistrar.php — Registers Gutenberg dynamic-block wrappers for the shortcodes
|
|
BlockPreview.php — Static editor-preview markup for the blocks
|
|
templates/ — PHP view files included by controllers/shortcodes
|
|
assets/ — CSS and JS (vanilla JS, no build step)
|
|
tests/Unit/ — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\)
|
|
Availability/ — Tests for src/Availability/
|
|
Booking/ — Tests for src/Booking/
|
|
Auth/ — Tests for src/Auth/
|
|
docs/features/ — One markdown file per feature describing data model, API, and test locations
|
|
```
|
|
|
|
**Code is organised package-by-domain** (Availability, Booking, Auth). Each domain package contains everything related to that domain: value objects, repositories, controllers, REST endpoints, and shortcode pages. Cross-cutting wiring classes (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) live directly under `src/`.
|
|
|
|
### Data Storage
|
|
Two custom database tables (created via `dbDelta` on activation):
|
|
- `{prefix}us_availability` — instructor availability windows
|
|
- `{prefix}us_lessons` — booked lessons
|
|
|
|
All database access goes through repository classes within their domain package. No direct `$wpdb` calls outside repositories.
|
|
|
|
### Key Classes
|
|
|
|
| Class | Responsibility |
|
|
|---|---|
|
|
| `Plugin` | Wires all components together on `plugins_loaded` |
|
|
| `Installer` | Creates DB tables and roles on activation |
|
|
| `Schema` | CREATE TABLE SQL strings for dbDelta |
|
|
| `AdminMenu` | Registers wp-admin menu pages |
|
|
| `RestRegistrar` | Registers all REST routes under `us-scheduler/v1` |
|
|
| `ShortcodeRegistrar` | Registers `[us_booking]` and `[us_student_login]` shortcodes |
|
|
| `BlockRegistrar` | Registers Gutenberg dynamic-block wrappers for the shortcodes |
|
|
| `BlockPreview` | Static editor-preview markup for the blocks |
|
|
| `Auth\RoleManager` | Registers `us_instructor` and `us_student` roles with custom caps |
|
|
| `Auth\LoginPage` | Renders front-end student login form |
|
|
| `Availability\AvailabilitySlot` | Immutable value object for a slot row |
|
|
| `Availability\AvailabilityRepository` | CRUD for availability slots |
|
|
| `Availability\AvailabilityController` | Instructor availability management page |
|
|
| `Availability\AvailabilityEndpoint` | REST handlers for availability CRUD |
|
|
| `Booking\Lesson` | Immutable value object for a lesson row |
|
|
| `Booking\BookingRepository` | CRUD for lesson bookings |
|
|
| `Booking\BookingEndpoint` | REST handlers for booking and status updates |
|
|
| `Booking\BookingPage` | Renders student booking UI shell (JS takes over) |
|
|
| `Booking\LessonController` | Admin and instructor lesson list pages |
|
|
|
|
### REST API Namespace
|
|
All endpoints live under `/wp-json/us-scheduler/v1/`. Permissions are enforced via `permission_callback` using capability checks (`manage_availability`, `book_lesson`), never role name checks.
|
|
|
|
### Testing Approach
|
|
Tests use [Brain\Monkey](https://brain-wp.github.io/BrainMonkey/) to stub WordPress functions without a full WP installation, and Mockery to mock `$wpdb` and other dependencies.
|
|
|
|
All test classes extend `tests/Unit/TestCase.php`, which handles `Monkey\setUp()` / `Monkey\tearDown()` and stubs all WP translation/escape functions automatically.
|
|
|
|
**Brain\Monkey API notes:**
|
|
- `Functions\when('fn')->alias(fn() => ...)` — stub with a closure (NOT `returnUsing()`)
|
|
- `Functions\when('fn')->justReturn($val)` — stub returning a fixed value
|
|
- `Functions\expect('fn')->once()->with(...)` — assert call count and arguments
|
|
- Use `Functions\when()` (not `Functions\expect()`) when you need argument-routing (e.g. `get_role` returning different values per argument) to avoid chaining ambiguity
|
|
- Mockery matchers (e.g. `\Mockery::type()`) inside plain PHP arrays do not work with `with()` — use `\Mockery::on(fn($arr) => ...)` or `\Mockery::any()` instead
|
|
- When mocking `$wpdb`, set `$mock->prefix = 'wp_'` explicitly — it is a public property, not a method
|
|
|
|
### Adding a Feature
|
|
1. Write the feature doc in `docs/features/<feature-name>.md` (data model, API, classes, test paths).
|
|
2. Create a domain package under `src/<Domain>/` containing all classes for that feature.
|
|
3. Add template(s) under `templates/` if needed.
|
|
4. Write unit tests under `tests/Unit/<Domain>/` mirroring the `src/<Domain>/` structure.
|
|
5. Run `composer test` — all tests must pass before the feature is complete.
|
|
|
|
### CI
|
|
Gitea Actions (`.gitea/workflows/ci.yml`) runs on every push and pull request:
|
|
- **lint** — PHPCS WordPress coding standards
|
|
- **static-analysis** — PHPStan level 6
|
|
- **test** — PHPUnit on PHP 8.1, 8.2, 8.3
|
|
- **no-debug** — rejects commits with `var_dump`, `error_log`, etc. in `src/`
|