mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 15:51:15 +00:00
feat(availability): per-staff working hours and class/session management
- Working-hours repo accepts nullable staff scope (pgx + memory); staff rules live in availability_rules and drive public slot generation - Protected routes for staff working hours, class templates, and class sessions with tenant-ownership checks and input validation - Sessions derive endsAt/capacity from templates; list resolves location names and live booked counts; template delete cascades sessions, session delete preserves bookings via SET NULL (mirrored in memory) - Zones page: staff-scope selector on working hours, new Classes tab with template/session CRUD, CS+EN strings, mobile tab wrapping - Normalize availability_rules times to HH:MM in the service layer Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d9aa29dbcb
commit
0405106587
12 files changed
+2069
-58
No files matched your search
@@ -287,6 +287,70 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/class-sessions": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["listClassSessions"];
|
||||
put?: never;
|
||||
post: operations["createClassSession"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/class-sessions/{sessionID}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete: operations["deleteClassSession"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/class-templates": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["listClassTemplates"];
|
||||
put?: never;
|
||||
post: operations["createClassTemplate"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/class-templates/{templateID}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete: operations["deleteClassTemplate"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/customers": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -415,6 +479,38 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/staff/{staffID}/working-hours": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["listStaffWorkingHours"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/staff/{staffID}/working-hours/{dayOfWeek}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put: operations["updateStaffWorkingHours"];
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/catalog/working-hours": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -895,6 +991,35 @@ export interface components {
|
||||
*/
|
||||
planCode: "starter" | "pro" | "business";
|
||||
};
|
||||
ClassSession: {
|
||||
bookedCount: number;
|
||||
capacity: number;
|
||||
/** Format: date-time */
|
||||
endsAt: string;
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
/** Format: uuid */
|
||||
locationId?: string;
|
||||
locationName?: string;
|
||||
/** Format: date-time */
|
||||
startsAt: string;
|
||||
/** Format: uuid */
|
||||
templateId: string;
|
||||
templateTitle: string;
|
||||
/** Format: uuid */
|
||||
tenantId: string;
|
||||
};
|
||||
ClassTemplate: {
|
||||
capacity: number;
|
||||
/** Format: date-time */
|
||||
createdAt: string;
|
||||
durationMinutes: number;
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
/** Format: uuid */
|
||||
tenantId: string;
|
||||
title: string;
|
||||
};
|
||||
CreateBookingRequest: {
|
||||
/** @enum {string} */
|
||||
bookingMode: "appointment" | "class";
|
||||
@@ -925,6 +1050,15 @@ export interface components {
|
||||
/** @enum {string} */
|
||||
status: "confirmed" | "waitlisted";
|
||||
};
|
||||
CreateClassSessionRequest: {
|
||||
capacity?: number;
|
||||
/** Format: uuid */
|
||||
locationId?: string;
|
||||
/** Format: date-time */
|
||||
startsAt: string;
|
||||
/** Format: uuid */
|
||||
templateId: string;
|
||||
};
|
||||
Customer: {
|
||||
bookingsCount?: number;
|
||||
/** Format: email */
|
||||
@@ -1222,6 +1356,11 @@ export interface components {
|
||||
/** @enum {string} */
|
||||
type: "closure" | "holiday" | "maintenance";
|
||||
};
|
||||
UpsertClassTemplateRequest: {
|
||||
capacity: number;
|
||||
durationMinutes: number;
|
||||
title: string;
|
||||
};
|
||||
UpsertCustomerRequest: {
|
||||
/** Format: email */
|
||||
email: string;
|
||||
@@ -1891,6 +2030,169 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
listClassSessions: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Upcoming class sessions */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ClassSession"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
createClassSession: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CreateClassSessionRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Class session created */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ClassSession"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid request */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Template or location not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
deleteClassSession: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
sessionID: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Class session deleted */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Class session not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
listClassTemplates: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Class templates */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ClassTemplate"][];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
createClassTemplate: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UpsertClassTemplateRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Class template created */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ClassTemplate"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid request */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
deleteClassTemplate: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
templateID: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Class template deleted */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Class template not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
listCustomers: {
|
||||
parameters: {
|
||||
query?: {
|
||||
@@ -2380,6 +2682,74 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
listStaffWorkingHours: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
staffID: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Staff member's weekly working hours */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["WorkingHours"][];
|
||||
};
|
||||
};
|
||||
/** @description Staff member not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
updateStaffWorkingHours: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
dayOfWeek: number;
|
||||
staffID: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["UpdateWorkingHoursRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Staff working hours updated */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Invalid request */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
/** @description Staff member not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content?: never;
|
||||
};
|
||||
};
|
||||
};
|
||||
listWorkingHours: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
Reference in new issue
Block a user