feat(branch): admin UI for branch working hours and rooms, plus real API docs
Three pages, all on the existing design system: BranchesPage lists the current environment's booking locations with their working-hours and active-room counts, and two subpages edit the week and the rooms. The list page deliberately does not create or rename a branch — clinic and doctor detail pages already do that, and duplicating it would give one physical place two edit surfaces. Route permission reuses `appointment_settings` rather than inventing a new one. Two real bugs fell out of exercising this end to end: `days` was serialising as a JSON *array*, not an object keyed "0".."6" — keys 0..6 are sequential so json_encode collapses them to a list. The client reads days["0"] either way, so nothing looked broken, but the response shape was unstable: one missing day would flip the same field to an object. The controller now casts to stdClass and WorkingHoursTest::testDaysIsAJsonObjectNotAnArray pins it. Found by curling the endpoint for the docs, not by any test. `<input type="time">` caps at 23:59, so it can neither display nor produce the legal end value 1440. An all-day range would have vanished from the form and been corrupted by the first save. Ranges now carry an explicit end-of-day flag, with a round-trip test proving 1440 survives. docs/api/branch.md documents all eight endpoints with responses captured from real curl runs against ddev, including the 422 and 404 bodies. doctor.md records that active/timezone now appear on all nine existing address endpoints (additive), and tenancy.md gains the two lessons this task taught: an aggregate child whose root is itself declared global inherits no environment and needs a real pair, and TenantFilter is not a substitute for an explicit ownership check because hard isolation only applies to a *chosen* context. Verified: phpunit 1067 tests / 2974 assertions green; slot-mode frozen contract green; phpstan 14 errors before and after, none in touched files; tsc clean; vitest 87 files / 612 tests green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -192,6 +192,29 @@ class WorkingHoursTest extends BranchTestCase
|
||||
self::assertSame(404, $this->responseCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* `days` باید **شیء** JSON با کلیدهای "0".."6" باشد، نه آرایه.
|
||||
* کلیدهای ۰..۶ پشتسرهماند و json_encode بیمراقبت آرایه میساخت؛ کلاینت
|
||||
* `days["0"]` هر دو را میخواند، ولی شکل پاسخ با جا افتادن یک روز عوض میشد.
|
||||
*/
|
||||
public function testDaysIsAJsonObjectNotAnArray(): void
|
||||
{
|
||||
[$user, , $address] = $this->doctorWithAddress();
|
||||
$this->put($user, $address->getUuid(), [0 => [['start_minute' => 540, 'end_minute' => 780]]]);
|
||||
|
||||
foreach (['PUT', 'GET'] as $method) {
|
||||
if ($method === 'GET') {
|
||||
$this->authJson('GET', "/api/v1/branch/{$address->getUuid()}/working-hours", $user);
|
||||
}
|
||||
|
||||
$raw = json_decode($this->client->getResponse()->getContent(), false);
|
||||
self::assertInstanceOf(\stdClass::class, $raw->data->days, "$method: days باید شیء باشد");
|
||||
// get_object_vars نامِ عددیِ ویژگیها را به int برمیگرداند؛ آنچه مهم است
|
||||
// stdClass بودن بالا سنجیده شد. اینجا فقط کامل بودن هفت روز.
|
||||
self::assertSame(range(0, 6), array_keys(get_object_vars($raw->data->days)));
|
||||
}
|
||||
}
|
||||
|
||||
public function testClinicOwnerManagesItsOwnBranch(): void
|
||||
{
|
||||
[$clinicUser, , $address] = $this->clinicWithAddress();
|
||||
|
||||
Reference in New Issue
Block a user