From ec7edbe6a8cfd130508624db38b907c83404dfea Mon Sep 17 00:00:00 2001 From: Ibrahim Chehab Date: Fri, 28 Aug 2026 12:18:37 -0400 Subject: [PATCH 1/2] Prayer spaces are manageable from the console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page was read-only, and its own doc comment said so: "the backend exposes GET /api/minbar/prayer-spaces and nothing to write with... a space can only be changed by editing the database directly." NOTES-FOR-BACKEND.md called it the single biggest hole. The backend now has admin CRUD, so this closes it. The page loads the admin list — every space, unfiltered — rather than the audience-scoped endpoint the app uses, because a console editing the sisters' musallah has to be able to open it whoever is signed in. The audience control stays as a client-side filter, now defaulting to All. **Type and audience are different questions**, and the form says so instead of leaving an operator to guess. Type is what kind of room it is: the label on the card and the filter on the map. Audience is who it is listed to. They agree for a gendered musallah and diverge for everything else — a multifaith room and a reflection bay are both shown to everyone. On create, audience follows the type until the operator overrides it; on edit it never moves on its own, so relabelling a sisters-only bay cannot quietly publish it to the whole campus. **Saves send only what changed.** The form diffs against a record refetched when the editor opens, so "I fixed a typo in step 3" is a patch to step 3 and not a full overwrite of somebody else's concurrent edit. Coordinates are handled as the pair the API treats them as: both or neither, emptying both sends `clearCoordinates` rather than silently dropping the edit, and a space without a pin says so plainly on the card and in the detail view rather than showing blanks — the app cannot navigate to it. Writes are gated on the new `board:prayerspace:write`. An account with only `board:content:read` gets the read-only view it has today, with the write affordances absent rather than disabled. The route guard is widened alongside the nav entry, since hiding a link is not authorization. Also here: `TopBar.jsx` loses a dead `production` variable that was failing lint. `npm run typecheck`, `npm run lint` and `npm run api:check` all pass. Create, edit, delete and the type→audience derivation verified against a live backend. Note for review: this repo's working tree carries a large, unrelated, uncommitted console rewrite, and several files here (`shell/nav.js`, `shell/TopBar.jsx`, `services/MinbarService.js`) belong to it — they appear as new because they have never been committed. This branch is a slice on top of that work and will not build against `develop` until the rest of it lands. Co-Authored-By: Claude Opus 5 --- NOTES-FOR-BACKEND.md | 147 + src/App.jsx | 264 +- src/api/schema.d.ts | 5632 +++++++++++++++----- src/components/board/PrayerSpaceEditor.jsx | 712 +++ src/components/board/index.js | 7 +- src/components/shell/TopBar.jsx | 60 + src/components/shell/nav.js | 158 + src/models/board.js | 51 + src/pages/PrayerSpaces.jsx | 441 ++ src/services/MinbarService.js | 61 + src/services/PrayerSpaceService.js | 330 ++ src/utils/permissions.js | 53 +- 12 files changed, 6343 insertions(+), 1573 deletions(-) create mode 100644 NOTES-FOR-BACKEND.md create mode 100644 src/components/board/PrayerSpaceEditor.jsx create mode 100644 src/components/shell/TopBar.jsx create mode 100644 src/components/shell/nav.js create mode 100644 src/pages/PrayerSpaces.jsx create mode 100644 src/services/MinbarService.js create mode 100644 src/services/PrayerSpaceService.js diff --git a/NOTES-FOR-BACKEND.md b/NOTES-FOR-BACKEND.md new file mode 100644 index 0000000..e26cca2 --- /dev/null +++ b/NOTES-FOR-BACKEND.md @@ -0,0 +1,147 @@ +# Gaps found while building the Minbar console + +Written against `LensBridgeBackend/openapi.yaml` as of 2026-08-24, while +rebuilding this repo into a pure management console. Everything below is a place +where the console either cannot do something an operator will ask for, or has to +work around the contract to do it. Ordered roughly by how much it hurts. + +None of this blocks the console shipping — each item has a workaround or an +honest "not possible from here" message in the UI. They are worth a look when +you're back. + +--- + +## 1. There is no user search + +`GET /api/admin/users` binds only a `Pageable`. There is no `q`, no `email`, no +`role` filter. The old console *sent* a `search` param and Spring discarded it +server-side, so user search has never worked — it just looked like it did. + +The new People page filters the page in hand and labels the input "Filter this +page", with a line under the table saying other pages are not covered. That is +honest at 34 accounts. It stops being adequate somewhere around 200. + +Cheapest fix: an optional `q` on the existing endpoint matching against +`firstName`, `lastName` and `email`. + +## 2. There is no "fetch one user" + +No `GET /api/admin/users/{userId}`. After a role grant or a permission revoke, +the console has to re-request the whole page it is on and pick the row back out +by id, purely to re-render one modal. See `refresh(userId)` in +`src/pages/People.jsx`. + +Also: the four grant endpoints return a `MessageResponse`, not the updated +`UserInfoResponse`. Returning the user would remove the refetch entirely. + +## 3. Audit filters do not compose + +Five endpoints — `/audit`, `/audit/action/{action}`, `/audit/daterange`, +`/audit/failed`, `/audit/upload/{uploadId}` — and each ignores the others' +criteria. "Failed permission grants in the last week", which is the query you +actually want during an incident, cannot be expressed. + +The console models this honestly: the scope is a single mutually-exclusive +choice and picking one clears the rest, because a row of checkboxes that +silently override each other is worse than a limitation you can see. + +One `GET /api/admin/audit` taking optional `action`, `start`, `end`, +`succeeded`, `adminId` and `targetEntityId` would replace all five. + +Related: `AuditAction` has an `EXPORT_DATA` constant but there is no export +endpoint, so nothing can ever emit it. + +## 4. Audit actions exist for operations that have no endpoint + +`AuditAction` declares `DISABLE_USER`, `ENABLE_USER`, `UNVERIFY_USER`, +`RESET_USER_PASSWORD`, `PROMOTE_USER`, `DEMOTE_USER`, `REMOVE_USER` and +`EXPORT_DATA`. None of them have a corresponding admin endpoint. + +The console can create a user and verify one. It cannot disable a compromised +account, un-verify a mistaken verification, or force a password reset — the +three things you actually want at 2am. Right now the answer is "edit the +database". + +`POST /api/admin/user/{userId}/disable` and `/enable` would be the highest-value +two. + +## 5. Media moderation has no bulk operations + +Approving a batch of forty photos after an event is forty round trips, each one +audited separately. `POST /api/admin/board/uploads/approve` taking a list of +ids would collapse that, and would make the audit log readable afterwards +("approved 40 uploads" instead of forty identical lines). + +## 6. Board content lists are unbounded + +`/api/admin/board/events`, `/posters`, `/socials` and `/weekly-content` all +return bare arrays. Events in particular only ever grow — by the third year +that's a several-hundred-element response on every load of the Content page, and +the console has to filter and sort the whole thing client-side. + +`/uploads` and `/users` already return `Page*`; these should too, or at least +take a date window. + +## 7. `GET /api/tcket/events` documents its Pageable wrong + +The controller is missing `@ParameterObject`, so springdoc writes the `Pageable` +into the spec as a single object-typed query parameter. A generated client +serializes that as `pageable[page]=0&pageable[size]=50`, which Spring's resolver +does not read — it wants flat `page`/`size`/`sort`. + +Already noted in your own `docs/API_CONTRACT.md`. `TicketingService.listEvents` +carries a custom `querySerializer` to flatten it. That workaround can go the +moment the annotation lands. + +## 8. `FrameType` serializes as one thing and is documented as another + +`FrameType` overrides `toString()` to return lowercase snake_case, and that is +what springdoc wrote into the spec (`poster`). Jackson serializes enums with +`name()` unless told otherwise, and nothing tells it otherwise — so the wire +format is `POSTER`. + +`normalizeFrameType` in `src/models/board.js` accepts both, so whichever side +gets fixed the console keeps working. But the contract is currently lying, and +anyone generating a client from it and trusting the enum will get an unlabelled +grey box. `FrameSlot` has no custom `toString()` and is unaffected — which is +why the slot chip resolved while the type did not. + +Either add `@JsonValue` to the lowercase form or drop the `toString()` override. + +## 9. Audience enum case + +`openapi.yaml` documents every `audience` as lowercase (`brothers`, `sisters`, +`both`) and the enum now serializes lowercase while still reading either case. +The console sends lowercase everywhere, including +`IssueEnrollmentTokenRequest` — the old enrollment page sent `BOTH`, which +worked only because of the lenient read. Worth confirming nothing else in the +system depends on the uppercase spelling before the leniency is removed. + +## 10. Small things + +- `GET /api/user/stats` does not exist, but the old Profile page called it on + every load and swallowed the failure. Removed here; mentioning it in case + another client still does. +- `POST /api/admin/user/create` takes `@RequestBody` without `@Valid`, so email + format and field lengths are unchecked server-side. The console mirrors the + column caps client-side, which is not the same as enforcing them. +- The four grant/revoke endpoints take a bare JSON string as the body + (`"BOARD_EDITOR"`). It works, but it types badly in every generated client — + `openapi-typescript` renders it as a literal union that no `string` satisfies, + so `IamService` carries two casts. A one-field wrapper object would be + friendlier. + +--- + +## What the console deliberately does not do + +For the avoidance of doubt, since these are absences by choice rather than gaps: + +- **No ticketing screens.** tCketManage has its own console on its own + subdomain. The only thing Minbar owns is the board-event to ticket-event + *link*, which needs `GET /api/tcket/events` to populate a picker and nothing + else. The sidebar links out. +- **No public surface.** No gallery, no submission form, no signup. Accounts are + created by an administrator through `POST /api/admin/user/create`. If you want + self-service signup back, `POST /api/auth/signup` is still live server-side — + nothing in this app calls it. diff --git a/src/App.jsx b/src/App.jsx index 7c3a584..4e8cca3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,120 +1,226 @@ -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { lazy, Suspense } from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import { ThemeProvider } from 'next-themes'; import { AuthProvider } from './context/AuthContext'; -import Header from './components/Header'; -import Footer from './components/Footer'; +import { ToastProvider, TooltipProvider, Spinner } from './components/ui'; +import ConsoleLayout from './components/shell/ConsoleLayout'; import ProtectedRoute from './components/ProtectedRoute'; import PermissionRoute from './components/PermissionRoute'; -import { - PERMISSIONS, - ADMIN_SECTION_PERMISSIONS, - BOARD_SECTION_PERMISSIONS, -} from './utils/permissions'; -import Home from './pages/Home'; -import Upload from './pages/Upload'; -import Gallery from './pages/Gallery'; -import AdminDashboard from './pages/AdminDashboard'; -import BoardManagement from './pages/BoardManagement'; -import DeviceList from './pages/DeviceList'; -import EnrollDevice from './pages/EnrollDevice'; -import DeviceDetail from './pages/DeviceDetail'; +import { PERMISSIONS } from './utils/permissions'; + import Login from './pages/Login'; -import Signup from './pages/Signup'; -import SignupSuccess from './pages/SignupSuccess'; -import ConfirmEmail from './pages/ConfirmEmail'; import ForgotPassword from './pages/ForgotPassword'; import ResetPassword from './pages/ResetPassword'; -import TermsOfService from './pages/TermsOfService'; -import PrivacyPolicy from './pages/PrivacyPolicy'; -import Profile from './pages/Profile'; -import './App.css'; +import ConfirmEmail from './pages/ConfirmEmail'; + +/** + * Console routes load on demand. + * + * Radix added roughly 56KB gzipped to a single bundle that already sat past + * Vite's size warning. Nobody opens all eleven of these in a session -- a + * BOARD_EDITOR may never open any of the access pages -- so the editors and + * their dependencies (the QR library, the STOMP client) should not be in the + * first paint. The four auth screens stay eager: they are what a signed-out + * visitor sees first, and a spinner before the login box is a bad trade. + */ +const Overview = lazy(() => import('./pages/Overview')); +const BoardContent = lazy(() => import('./pages/BoardContent')); +const Displays = lazy(() => import('./pages/Displays')); +const DisplayDetail = lazy(() => import('./pages/DisplayDetail')); +const PrayerSpaces = lazy(() => import('./pages/PrayerSpaces')); +const MediaQueue = lazy(() => import('./pages/MediaQueue')); +const People = lazy(() => import('./pages/People')); +const Roles = lazy(() => import('./pages/Roles')); +const AuditLog = lazy(() => import('./pages/AuditLog')); +const Account = lazy(() => import('./pages/Account')); +const NotFound = lazy(() => import('./pages/NotFound')); -function App() { + +/** + * The Minbar management console. + * + * Two frames and nothing else: `AuthLayout` for the four screens you can reach + * signed out, `ConsoleLayout` for everything else. There is no public surface — + * no gallery, no submission form, no marketing pages. Accounts are created by + * an administrator, which is why `/signup` is gone rather than hidden. + * + * Route gates repeat the permission lists that `components/shell/nav.js` uses + * to build the sidebar. That duplication is deliberate: hiding a link is not + * authorization, and someone with a bookmark reaches the route directly. + * + * Each gate asks for the narrowest permission that makes the page worth + * opening — a read, in every case. The controls inside gate themselves; see + * `utils/permissions.js`. + */ +/** Holds the layout's shape while a route chunk arrives, so nothing jumps. */ +function RouteFallback() { return ( +
+ +
+ ); +} + +/** + * Where the chosen theme is remembered. + * + * `index.html` reads this same key in a blocking script before first paint, so + * the two must not drift — a rename here without one there costs you a white + * flash on every load in dark mode, and nothing else, which is exactly the kind + * of bug that ships. + */ +const THEME_STORAGE_KEY = 'minbar-theme'; + +export default function App() { + return ( + /* + `attribute="class"` to match the `dark` variant in index.css, and + `disableTransitionOnChange` because roughly every surface in this console + carries a colour transition for its hover state: without it, flipping the + theme animates all of them at once and the whole page wipes over 150ms + instead of switching. + */ + - -
-
-
- - } /> - + + + + } /> + } /> + } /> + } /> + + + }> + + + + } + > + } /> + + - - - } + + + + } /> - } /> - - - - } + + + + } + /> + + + + } /> - + + } /> - {/* - Board routes gate on the narrowest permission that makes the page - worth opening — a read, in every case but enrollment, which has - nothing to show someone who cannot mint a token. Controls inside - each page gate themselves; see src/utils/permissions.js. - */} + - + + } /> + - + + } /> - + + } /> - + + } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
-
-
-
+ + } /> + + {/* Paths from the previous app, kept so live bookmarks land somewhere. */} + } /> + } /> + } /> + } /> + + } /> + + + + +
+
); } - -export default App; diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts index ea1c932..fd1f1ad 100644 --- a/src/api/schema.d.ts +++ b/src/api/schema.d.ts @@ -288,10 +288,44 @@ export interface paths { get: operations["getBoardEventById"]; put?: never; post?: never; - delete: operations["deleteEvent"]; + delete: operations["deleteEvent_1"]; + options?: never; + head?: never; + patch: operations["updateEvent_1"]; + trace?: never; + }; + "/api/admin/board/events/{eventId}/ticket-event": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Unlink the tCketManage event from a board event */ + delete: operations["unlinkTicketEvent"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/admin/board/events/{eventId}/ticket-event/{tcketEventId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** Link a tCketManage event to a board event */ + put: operations["linkTicketEvent"]; + post?: never; + delete?: never; options?: never; head?: never; - patch: operations["updateEvent"]; + patch?: never; trace?: never; }; "/api/admin/board/posters": { @@ -422,14 +456,15 @@ export interface paths { patch: operations["updateSocial"]; trace?: never; }; - "/api/admin/board/weekly-content": { + "/api/admin/board/uploads": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getAllWeeklyContent"]; + /** Page through every upload, any approval state */ + get: operations["getAdminUploads"]; put?: never; post?: never; delete?: never; @@ -438,14 +473,14 @@ export interface paths { patch?: never; trace?: never; }; - "/api/admin/board/weekly-content/year/{year}": { + "/api/admin/board/uploads/approved": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getWeeklyContentByYear"]; + get: operations["getApprovedUploads"]; put?: never; post?: never; delete?: never; @@ -454,58 +489,55 @@ export interface paths { patch?: never; trace?: never; }; - "/api/admin/board/weekly-content/{year}/{weekNumber}": { + "/api/admin/board/uploads/featured": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** Fetch weekly content as an administrator */ - get: operations["getAdminWeeklyContent"]; - put: operations["saveWeeklyContent"]; + get: operations["getFeaturedUploads"]; + put?: never; post?: never; - delete: operations["deleteWeeklyContent"]; + delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/create-event": { + "/api/admin/board/uploads/pending": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get?: never; + get: operations["getPendingUploads"]; put?: never; - /** Create a media event */ - post: operations["createMediaEvent"]; + post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/events": { + "/api/admin/board/uploads/{uploadId}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** List every event, including unpublished ones */ - get: operations["getAllEventsForAdmin"]; + get?: never; put?: never; post?: never; - delete?: never; + delete: operations["deleteUpload"]; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/feature-upload/{uploadId}": { + "/api/admin/board/uploads/{uploadId}/approval": { parameters: { query?: never; header?: never; @@ -514,48 +546,46 @@ export interface paths { }; get?: never; put?: never; - post: operations["featureUpload"]; - delete?: never; + post?: never; + delete: operations["unapproveUpload"]; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/permissions": { + "/api/admin/board/uploads/{uploadId}/approve": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** List every permission, for building a direct-grant picker */ - get: operations["getAvailablePermissions"]; + get?: never; put?: never; - post?: never; + post: operations["approveUpload"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/roles": { + "/api/admin/board/uploads/{uploadId}/feature": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** List assignable roles with the permissions each one confers */ - get: operations["getAvailableRoles"]; + get?: never; put?: never; - post?: never; + post: operations["featureUpload"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/upload/{uploadId}": { + "/api/admin/board/uploads/{uploadId}/featured": { parameters: { query?: never; header?: never; @@ -564,86 +594,108 @@ export interface paths { }; get?: never; put?: never; - post: operations["approveUpload"]; - delete: operations["deleteUpload"]; + post?: never; + delete: operations["unfeatureUpload"]; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/upload/{uploadId}/approval": { + "/api/admin/board/weekly-content": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get?: never; + get: operations["getAllWeeklyContent"]; put?: never; post?: never; - delete: operations["unapproveUpload"]; + delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/upload/{uploadId}/featured": { + "/api/admin/board/weekly-content/year/{year}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get?: never; + get: operations["getWeeklyContentByYear"]; put?: never; post?: never; - delete: operations["unfeatureUpload"]; + delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/uploads": { + "/api/admin/board/weekly-content/{year}/{weekNumber}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** Page through every upload, any approval state */ - get: operations["getAdminUploads"]; - put?: never; + /** Fetch weekly content as an administrator */ + get: operations["getAdminWeeklyContent"]; + put: operations["saveWeeklyContent"]; post?: never; - delete?: never; + delete: operations["deleteWeeklyContent"]; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/uploads/approved": { + "/api/admin/minbar/prayer-spaces": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getApprovedUploads"]; + /** List every prayer space, regardless of audience */ + get: operations["listAdminPrayerSpaces"]; put?: never; - post?: never; + /** Create a prayer space */ + post: operations["createPrayerSpace"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/admin/uploads/featured": { + "/api/admin/minbar/prayer-spaces/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getFeaturedUploads"]; + /** Fetch one prayer space as an administrator */ + get: operations["getAdminPrayerSpace"]; + put?: never; + post?: never; + /** Delete a prayer space and its directions */ + delete: operations["deletePrayerSpace"]; + options?: never; + head?: never; + /** Update a prayer space; omitted fields are left unchanged */ + patch: operations["updatePrayerSpace"]; + trace?: never; + }; + "/api/admin/permissions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List every permission, for building a direct-grant picker */ + get: operations["getAvailablePermissions"]; put?: never; post?: never; delete?: never; @@ -652,14 +704,15 @@ export interface paths { patch?: never; trace?: never; }; - "/api/admin/uploads/pending": { + "/api/admin/roles": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getPendingUploads"]; + /** List assignable roles with the permissions each one confers */ + get: operations["getAvailableRoles"]; put?: never; post?: never; delete?: never; @@ -1011,32 +1064,15 @@ export interface paths { patch?: never; trace?: never; }; - "/api/events": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** List publicly visible events */ - get: operations["getPublicEvents"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/events/{id}": { + "/api/minbar/events": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** Fetch a single publicly visible event */ - get: operations["getPublicEventById"]; + /** List events for a one-month window, enriched with ticket availability when tCketManage is enabled */ + get: operations["getMinbarEvents"]; put?: never; post?: never; delete?: never; @@ -1045,15 +1081,15 @@ export interface paths { patch?: never; trace?: never; }; - "/api/gallery": { + "/api/minbar/prayer-spaces": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - /** Page through approved gallery media */ - get: operations["getGalleryUploads"]; + /** List prayer spaces for an audience, with directions */ + get: operations["getMinbarPrayerSpaces"]; put?: never; post?: never; delete?: never; @@ -1062,14 +1098,15 @@ export interface paths { patch?: never; trace?: never; }; - "/api/gallery/event/{eventId}": { + "/api/minbar/prayer-spaces/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getGalleryByEvent"]; + /** Fetch a single prayer space with directions */ + get: operations["getMinbarPrayerSpaceById"]; put?: never; post?: never; delete?: never; @@ -1176,14 +1213,14 @@ export interface paths { patch?: never; trace?: never; }; - "/api/upload/event/{eventId}": { + "/api/tcket/email-jobs/{jobId}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUploadsByEvent"]; + get: operations["getJob"]; put?: never; post?: never; delete?: never; @@ -1192,23 +1229,23 @@ export interface paths { patch?: never; trace?: never; }; - "/api/upload/limits": { + "/api/tcket/events": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUploadLimits"]; + get: operations["getAllEvents"]; put?: never; - post?: never; + post: operations["createEvent"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/upload/{eventId}/direct/complete": { + "/api/tcket/events/full": { parameters: { query?: never; header?: never; @@ -1217,14 +1254,30 @@ export interface paths { }; get?: never; put?: never; - post: operations["completeDirectUpload"]; + post: operations["createFullEvent"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/upload/{eventId}/direct/presign": { + "/api/tcket/events/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getEventById"]; + put: operations["updateEvent"]; + post?: never; + delete: operations["deleteEvent"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/events/{id}/imports": { parameters: { query?: never; header?: never; @@ -1233,846 +1286,3327 @@ export interface paths { }; get?: never; put?: never; - post: operations["generatePresignedUploadUrl"]; + post: operations["importAttendees"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/upload/{uploadId}": { + "/api/tcket/events/{id}/ticket-types": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUploadById"]; + get: operations["getTicketTypesByEvent"]; put?: never; - post?: never; + post: operations["createTicketType"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/user/profile": { + "/api/tcket/events/{id}/tickets": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUserProfile"]; + get: operations["getTicketsByEvent"]; put?: never; post?: never; delete?: never; options?: never; head?: never; - patch: operations["updateUserProfile"]; + patch?: never; trace?: never; }; - "/api/user/stats": { + "/api/tcket/events/{id}/tickets/resend": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUserStats"]; + get?: never; put?: never; - post?: never; + post: operations["resendAllTickets"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/user/uploads": { + "/api/tcket/events/{id}/tickets/send-missing": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get: operations["getUserUploads"]; + get?: never; put?: never; - post?: never; + post: operations["sendMissingTickets"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/user/uploads/{uploadId}": { + "/api/tcket/events/{id}/zones": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - get?: never; + get: operations["getZonesByEvent"]; put?: never; - post?: never; - delete: operations["deleteUserUpload"]; + post: operations["addZoneToEvent"]; + delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; -} -export type webhooks = Record; -export interface components { - schemas: { - AdminUploadDto: { - anon?: boolean; - approved?: boolean; - /** @enum {string} */ - contentType?: "image" | "video" | "audio" | "document"; - /** Format: date-time */ - createdDate?: string; - displayName?: string; - /** Format: uuid */ - eventId?: string; - eventName?: string; - featured?: boolean; - fileName?: string; - fileUrl?: string; - instagramHandle?: string; - secureUrl?: string; - thumbnailUrl?: string; - uploadDescription?: string; - /** Format: uuid */ - uploadedBy?: string; - uploaderEmail?: string; - uploaderFirstName?: string; - uploaderFullName?: string; - uploaderLastName?: string; - uploaderStudentNumber?: string; - /** Format: uuid */ - uuid?: string; - }; - AgendaFrameConfig: Omit & { - days?: components["schemas"]["DayBucket"][]; - heading?: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "agenda"; + "/api/tcket/orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - AgentEnrollRequest: { - agentVersion?: string; - hardwareModel?: string; - hostname: string; - publicKey: string; - token: string; + get: operations["getOrders"]; + put?: never; + post: operations["createOrder_1"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/orders/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - AgentEnrollResponse: { - /** Format: uuid */ - deviceId?: string; - websocketUrl?: string; + get: operations["getOrder_1"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/orders/{id}/cancel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - AuditEventDto: { - /** @enum {string} */ - action?: "APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER"; - adminEmail?: string; - /** Format: uuid */ - adminId?: string; - adminName?: string; - details?: string; - /** Format: uuid */ - id?: string; - ipAddress?: string; + get?: never; + put?: never; + post: operations["cancelOrder_1"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/orders/{id}/confirm-manual-payment": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["confirmManualPayment"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/payments/mock/{orderId}/complete": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["completeMockPayment"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/payments/{providerId}/webhook": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["webhook"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/count/ticket/{ticketId}/zone/{zoneId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getTicketZoneEntryCount"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/event/{eventId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getScanHistoryForEvent"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/scan": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["scanTicket"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/scan-qr": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["scanByQr"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/ticket/{ticketId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getScanHistoryForTicket"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/validate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["validateTicketForZone"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/scans/zone/{zoneId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getScanHistoryForZone"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/ticket-types": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getAllTicketTypes"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/ticket-types/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getTicketTypeById"]; + put: operations["updateTicketType"]; + post?: never; + delete: operations["deleteTicketType"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/tickets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["createTicket"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/tickets/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getTicketById"]; + put: operations["updateTicket"]; + post?: never; + delete: operations["deleteTicket"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/tickets/{id}/resend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["resendTicket"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/zones": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getAllZones"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/tcket/zones/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getZoneById"]; + put: operations["updateZone"]; + post?: never; + delete: operations["deleteZone"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/upload/event/{eventId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUploadsByEvent"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/upload/limits": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUploadLimits"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/upload/{eventId}/direct/complete": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["completeDirectUpload"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/upload/{eventId}/direct/presign": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["generatePresignedUploadUrl"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/upload/{uploadId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUploadById"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUserOrders"]; + put?: never; + post: operations["createOrder"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/orders/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getOrder"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/orders/{id}/cancel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["cancelOrder"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/profile": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUserProfile"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch: operations["updateUserProfile"]; + trace?: never; + }; + "/api/user/tickets": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUserTickets"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/uploads": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getUserUploads"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/user/uploads/{uploadId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: operations["deleteUserUpload"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + AddZoneRequest: { + zoneName: string; + }; + AdminUploadDto: { + anon?: boolean; + approved?: boolean; + /** @enum {string} */ + contentType?: "image" | "video" | "audio" | "document"; + /** Format: date-time */ + createdDate?: string; + displayName?: string; + /** Format: uuid */ + eventId?: string; + eventName?: string; + featured?: boolean; + fileName?: string; + fileUrl?: string; + instagramHandle?: string; + secureUrl?: string; + thumbnailUrl?: string; + uploadDescription?: string; + /** Format: uuid */ + uploadedBy?: string; + uploaderEmail?: string; + uploaderFirstName?: string; + uploaderFullName?: string; + uploaderLastName?: string; + uploaderStudentNumber?: string; + /** Format: uuid */ + uuid?: string; + }; + AgendaFrameConfig: Omit & { + days?: components["schemas"]["DayBucket"][]; + heading?: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "agenda"; + }; + AgentEnrollRequest: { + agentVersion?: string; + hardwareModel?: string; + hostname: string; + publicKey: string; + token: string; + }; + AgentEnrollResponse: { + /** Format: uuid */ + deviceId?: string; + websocketUrl?: string; + }; + AuditEventDto: { + /** @enum {string} */ + action?: "APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "LINK_TICKET_EVENT" | "UNLINK_TICKET_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_PRAYER_SPACE" | "UPDATE_PRAYER_SPACE" | "DELETE_PRAYER_SPACE" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER"; + adminEmail?: string; + /** Format: uuid */ + adminId?: string; + adminName?: string; + details?: string; + /** Format: uuid */ + id?: string; + ipAddress?: string; + /** Format: uuid */ + targetEntityId?: string; + /** @enum {string} */ + targetEntityType?: "USER" | "UPLOAD" | "MUSALLAH_BOARD" | "DEVICE" | "PRAYER_SPACE" | "EVENT"; + /** Format: date-time */ + timestamp?: string; + userAgent?: string; + }; + BoardEvent: { + allDay?: boolean; + allowUploads?: boolean; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + description?: string; + /** Format: date-time */ + endTime?: string; + event?: components["schemas"]["Event"]; + /** Format: uuid */ + id?: string; + location?: string; + name?: string; + /** Format: date-time */ + startTime?: string; + }; + BoardingPassView: { + attendeeName?: string; + eventDate?: string; + /** Format: uuid */ + eventId?: string; + eventTitle?: string; + location?: string; + qrPayload?: string; + startTime?: string; + status?: string; + /** Format: uuid */ + ticketId?: string; + ticketType?: string; + }; + ChangePasswordRequest: { + currentPassword: string; + newPassword: string; + }; + CommandIssuedResponse: { + /** Format: uuid */ + commandId?: string; + /** Format: uuid */ + deviceId?: string; + /** Format: date-time */ + expiresAt?: string; + /** Format: date-time */ + issuedAt?: string; + kind?: string; + /** @enum {string} */ + status?: "PENDING" | "DELIVERED" | "ACKED" | "RUNNING" | "SUCCEEDED" | "FAILED" | "TIMEOUT" | "REJECTED" | "EXPIRED"; + }; + CommandView: { + /** Format: date-time */ + ackedAt?: string; + /** Format: int32 */ + deadlineMs?: number; + /** Format: date-time */ + deliveredAt?: string; + /** Format: uuid */ + deviceId?: string; + errorMessage?: string; + /** Format: date-time */ + expiresAt?: string; + /** Format: date-time */ + finishedAt?: string; + /** Format: uuid */ + id?: string; + /** Format: date-time */ + issuedAt?: string; + issuedBy?: string; + kind?: string; + output?: components["schemas"]["JsonNode"]; + payload?: components["schemas"]["JsonNode"]; + /** Format: date-time */ + startedAt?: string; + /** @enum {string} */ + status?: "PENDING" | "DELIVERED" | "ACKED" | "RUNNING" | "SUCCEEDED" | "FAILED" | "TIMEOUT" | "REJECTED" | "EXPIRED"; + }; + CreateCalendarEventRequest: { + allDay?: boolean; + allowUploads?: boolean; + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + description: string; + /** Format: int64 */ + endEpochMs: number; + location: string; + name: string; + /** Format: int64 */ + startEpochMs: number; + }; + CreateEventRequest: { + description: string; + location: string; + name: string; + /** Format: date-time */ + time: string; + }; + CreateFullEventRequest: { + description: string; + location: string; + name: string; + ticketTypes?: components["schemas"]["WizardTicketTypeRequest"][]; + /** Format: date-time */ + time: string; + zones: components["schemas"]["WizardZoneRequest"][]; + }; + CreateOrderRequest: { + /** Format: email */ + buyerEmail: string; + /** Format: uuid */ + eventId: string; + items: components["schemas"]["OrderItemRequest"][]; + providerId?: string; + }; + CreatePosterRequest: { + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + /** Format: date-time */ + endTime: string; + /** Format: binary */ + imageFile: string; + signupUrl?: string; + /** Format: date-time */ + startTime: string; + title: string; + }; + CreatePrayerSpaceRequest: { + amenities?: string[]; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + building: string; + /** Format: int32 */ + capacity?: number; + directions?: string; + entranceDescription?: string; + entranceName?: string; + floor?: string; + imageUrl?: string; + /** Format: double */ + latitude?: number; + /** Format: double */ + longitude?: number; + mapsUrl?: string; + name: string; + notes?: string; + roomInfo?: string; + startingPoint?: string; + steps?: components["schemas"]["DirectionStepRequest"][]; + tag: string; + tips?: string[]; + /** @enum {string} */ + type: "brothers" | "sisters" | "multifaith" | "reflection"; + /** Format: int32 */ + walkTimeMinutes?: number; + }; + CreatePromotableSocialMediaRequest: { + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + footerText: string; + handle?: string; + headerText: string; + heroText: string; + name: string; + /** @enum {string} */ + type: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; + url: string; + }; + CreateTicketRequest: { + /** Format: email */ + email: string; + /** Format: uuid */ + eventId: string; + firstName: string; + lastName: string; + sendEmail?: boolean; + /** Format: uuid */ + ticketTypeId: string; + }; + CreateTicketTypeRequest: { + entitlements?: components["schemas"]["ZoneEntitlementRequest"][]; + isActive?: boolean; + name: string; + price: number; + /** Format: date-time */ + salesEndAt?: string; + /** Format: date-time */ + salesStartAt?: string; + }; + CreateUserRequest: { + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + /** Format: email */ + email: string; + firstName: string; + lastName: string; + /** @description Optional. Omit it to create a disabled account and email the user a password reset link; supply it to create an account that can sign in right away. */ + password?: string; + }; + DayBucket: { + /** Format: date */ + date?: string; + events?: components["schemas"]["EventView"][]; + }; + DeviceConfig: { + /** Format: int32 */ + agendaDurationSeconds?: number; + darkModeAfterIsha?: boolean; + enableScrollingMessage?: boolean; + /** Format: uuid */ + id?: string; + location?: components["schemas"]["Location"]; + /** Format: int32 */ + nextPrayerDurationSeconds?: number; + scrollingMessages?: string[]; + }; + DeviceSummary: { + agentVersion?: string; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + displayName?: string; + /** Format: date-time */ + enrolledAt?: string; + hardwareModel?: string; + /** Format: uuid */ + id?: string; + /** Format: date-time */ + lastHeartbeat?: string; + lastSeenIp?: string; + /** Format: date-time */ + revokedAt?: string; + }; + DirectionStepRequest: { + instruction: string; + subtext?: string; + }; + DirectionStepView: { + /** Format: uuid */ + id?: string; + instruction?: string; + /** Format: int32 */ + order?: number; + subtext?: string; + }; + EmailJobAccepted: { + /** Format: uuid */ + jobId?: string; + /** Format: int32 */ + total?: number; + }; + EmailJobStatus: { + /** Format: int32 */ + failed?: number; + /** Format: date-time */ + finishedAt?: string; + /** Format: uuid */ + jobId?: string; + lastEmail?: string; + lastSuccess?: boolean; + /** Format: int32 */ + processed?: number; + /** Format: int32 */ + sent?: number; + /** Format: date-time */ + startedAt?: string; + /** @enum {string} */ + state?: "RUNNING" | "COMPLETED"; + /** Format: int32 */ + total?: number; + type?: string; + }; + Event: { + description: string; + /** Format: uuid */ + id?: string; + location: string; + name: string; + /** Format: date-time */ + time: string; + }; + EventResponse: { + description?: string; + /** Format: uuid */ + id?: string; + location?: string; + name?: string; + /** Format: date-time */ + time?: string; + zones?: components["schemas"]["ZoneResponse"][]; + }; + EventView: { + allDay?: boolean; + allowUploads?: boolean; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + description?: string; + /** Format: date-time */ + endTime?: string; + /** Format: uuid */ + id?: string; + location?: string; + name?: string; + /** Format: date-time */ + startTime?: string; + /** Format: uuid */ + ticketEventId?: string; + ticketTypes?: components["schemas"]["MinbarTicketTypeView"][]; + }; + FrameConfig: { + type: string; + }; + FrameDefinition: { + /** Format: int32 */ + durationInSeconds?: number; + frameConfig?: components["schemas"]["AgendaFrameConfig"] | components["schemas"]["IslamicQuoteFrameConfig"] | components["schemas"]["JummahFrameConfig"] | components["schemas"]["NextPrayerFrameConfig"] | components["schemas"]["PosterFrameConfig"] | components["schemas"]["PromotableSocialMediaFrameConfig"]; + frameId?: string; + /** @enum {string} */ + frameType?: "poster" | "next_prayer" | "agenda" | "jummah" | "islamic_quote" | "socials"; + }; + FullEventResponse: { + event?: components["schemas"]["EventResponse"]; + ticketTypes?: components["schemas"]["TicketTypeResponse"][]; + }; + ImportConfig: { + /** Format: uuid */ + defaultTicketTypeId?: string; + /** Format: int32 */ + emailColumn: number; + /** Format: int32 */ + firstNameColumn: number; + hasHeaderRow?: boolean; + /** Format: int32 */ + lastNameColumn: number; + /** Format: int32 */ + ticketTypeColumn?: number; + }; + ImportResult: { + errors?: components["schemas"]["RowError"][]; + /** Format: int32 */ + imported?: number; + }; + IslamicQuote: { + arabic?: string; + /** Format: int32 */ + durationSeconds?: number; + /** Format: uuid */ + id?: string; + /** @enum {string} */ + kind?: "VERSE" | "HADITH"; + reference?: string; + translation?: string; + transliteration?: string; + }; + IslamicQuoteFrameConfig: Omit & { + arabic?: string; + /** @enum {string} */ + kind?: "VERSE" | "HADITH"; + reference?: string; + translation?: string; + transliteration?: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "islamic_quote"; + }; + IssueCommandRequest: { + /** Format: int32 */ + deadlineMs?: number; + kind: string; + payload?: components["schemas"]["JsonNode"]; + /** Format: int32 */ + ttlSeconds?: number; + }; + IssueEnrollmentTokenRequest: { + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + displayName: string; + /** Format: int32 */ + expiresInMinutes?: number; + }; + IssueEnrollmentTokenResponse: { + /** Format: date-time */ + expiresAt?: string; + token?: string; + /** Format: uuid */ + tokenId?: string; + }; + JsonNode: unknown; + JummahFrameConfig: Omit & { + prayers?: components["schemas"]["JummahSlot"][]; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "jummah"; + }; + JummahPrayer: { + /** Format: uuid */ + id?: string; + khatib?: string; + prayerTime?: string; + room?: string; + }; + JummahSlot: { + khatib?: string; + prayerTime?: string; + room?: string; + }; + JwtResponse: { + email?: string; + firstName?: string; + /** Format: uuid */ + id?: string; + lastName?: string; + permissions?: string[]; + refreshToken?: string; + roles?: string[]; + token?: string; + type?: string; + }; + Location: { + city?: string; + country?: string; + /** Format: double */ + latitude?: number; + /** Format: double */ + longitude?: number; + /** @enum {string} */ + method?: "KARACHI" | "ISNA" | "MWL" | "MAKKAH" | "EGYPT" | "TEHRAN" | "GULF" | "KUWAIT" | "QATAR" | "SINGAPORE" | "FRANCE" | "TURKEY" | "RUSSIA" | "DUBAI"; + timezone?: string; + }; + LoginRequest: { + /** Format: email */ + email: string; + password: string; + }; + MessageResponse: { + message?: string; + }; + MinbarCreateOrderRequest: { + /** Format: uuid */ + eventId: string; + items: components["schemas"]["OrderItemRequest"][]; + }; + MinbarOrderItemResponse: { + attendeeFirstName?: string; + attendeeLastName?: string; + /** Format: uuid */ + eventId?: string; + ticketTypeName?: string; + unitPrice?: number; + }; + MinbarOrderResponse: { + amountTotal?: number; + buyerEmail?: string; + /** Format: date-time */ + createdAt?: string; + currency?: string; + /** Format: date-time */ + expiresAt?: string; + /** Format: uuid */ + id?: string; + items?: components["schemas"]["MinbarOrderItemResponse"][]; + /** Format: date-time */ + paidAt?: string; + payment?: components["schemas"]["MinbarPaymentResponse"]; + referenceCode?: string; + status?: string; + }; + MinbarPaymentResponse: { + details?: { + [key: string]: string; + }; + instructions?: string; + redirectUrl?: string; + type?: string; + }; + MinbarTicketTypeView: { + /** Format: uuid */ + id?: string; + name?: string; + price?: number; + /** Format: date-time */ + salesEndAt?: string; + /** Format: date-time */ + salesStartAt?: string; + soldOut?: boolean; + }; + MusallahBoardPayload: { + deviceConfig?: components["schemas"]["DeviceConfig"]; + frames?: components["schemas"]["FrameDefinition"][]; + weather?: components["schemas"]["JsonNode"]; + }; + NextPrayerFrameConfig: Omit & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "next_prayer"; + }; + OrderItemRequest: { + /** Format: email */ + attendeeEmail: string; + attendeeFirstName: string; + attendeeLastName: string; + /** Format: uuid */ + ticketTypeId: string; + }; + OrderItemResponse: { + attendeeEmail?: string; + attendeeFirstName?: string; + attendeeLastName?: string; + /** Format: uuid */ + eventId?: string; + /** Format: uuid */ + id?: string; + /** Format: uuid */ + ticketTypeId?: string; + ticketTypeName?: string; + unitPrice?: number; + }; + OrderResponse: { + amountTotal?: number; + buyerEmail?: string; + /** Format: date-time */ + createdAt?: string; + currency?: string; + /** Format: uuid */ + eventId?: string; + /** Format: date-time */ + expiresAt?: string; + externalRef?: string; + /** Format: uuid */ + id?: string; + items?: components["schemas"]["OrderItemResponse"][]; + /** Format: date-time */ + paidAt?: string; + payment?: components["schemas"]["PaymentResponse"]; + providerId?: string; + referenceCode?: string; + status?: string; + }; + PageAdminUploadDto: { + content?: components["schemas"]["AdminUploadDto"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageAuditEventDto: { + content?: components["schemas"]["AuditEventDto"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageEventResponse: { + content?: components["schemas"]["EventResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageScanEventResponse: { + content?: components["schemas"]["ScanEventResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageTicketResponse: { + content?: components["schemas"]["TicketResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageTicketTypeResponse: { + content?: components["schemas"]["TicketTypeResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageUploadDto: { + content?: components["schemas"]["UploadDto"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageUserInfoResponse: { + content?: components["schemas"]["UserInfoResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + PageZoneResponse: { + content?: components["schemas"]["ZoneResponse"][]; + empty?: boolean; + first?: boolean; + last?: boolean; + /** Format: int32 */ + number?: number; + /** Format: int32 */ + numberOfElements?: number; + pageable?: components["schemas"]["Pageable"]; + /** Format: int32 */ + size?: number; + sort?: components["schemas"]["SortObject"]; + /** Format: int64 */ + totalElements?: number; + /** Format: int32 */ + totalPages?: number; + }; + Pageable: { + /** Format: int32 */ + page?: number; + /** Format: int32 */ + size?: number; + sort?: string[]; + }; + PaymentResponse: { + details?: { + [key: string]: string; + }; + instructions?: string; + providerRef?: string; + redirectUrl?: string; + type?: string; + }; + PermissionResponse: { + authority?: string; + domain?: string; + name?: string; + }; + Poster: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + /** Format: date-time */ + endTime?: string; + /** Format: uuid */ + id?: string; + image?: string; + signupUrl?: string; + /** Format: date-time */ + startTime?: string; + title?: string; + }; + PosterFrameConfig: Omit & { + posterUrl?: string; + signupUrl?: string; + title?: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "poster"; + }; + PrayerSpaceView: { + amenities?: string[]; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + building?: string; + /** Format: int32 */ + capacity?: number; + directions?: string; + entranceDescription?: string; + entranceName?: string; + floor?: string; + /** Format: uuid */ + id?: string; + imageUrl?: string; + /** Format: double */ + latitude?: number; + /** Format: double */ + longitude?: number; + mapsUrl?: string; + name?: string; + notes?: string; + roomInfo?: string; + startingPoint?: string; + steps?: components["schemas"]["DirectionStepView"][]; + tag?: string; + tips?: string[]; + /** @enum {string} */ + type?: "brothers" | "sisters" | "multifaith" | "reflection"; + /** Format: int32 */ + walkTimeMinutes?: number; + }; + PresignedUploadResponse: { + contentType?: string; + /** Format: uuid */ + eventId?: string; + expectedSha256?: string; + /** Format: int32 */ + expiresInMinutes?: number; + method?: string; + objectKey?: string; + uploadUrl?: string; + }; + PromotableSocialMedia: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + footerText?: string; + handle?: string; + headerText?: string; + heroText?: string; + /** Format: uuid */ + id?: string; + name?: string; + /** @enum {string} */ + type?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; + url?: string; + }; + PromotableSocialMediaFrameConfig: Omit & { + footerText?: string; + handle?: string; + headerText?: string; + heroText?: string; + /** @enum {string} */ + socialType?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; + url?: string; + } & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + type: "socials"; + }; + QrScanRequest: { + qrPayload: string; + /** Format: uuid */ + zoneId: string; + }; + QuoteEntry: { + arabic?: string; + /** Format: int32 */ + durationSeconds?: number; + /** @enum {string} */ + kind?: "VERSE" | "HADITH"; + reference?: string; + translation?: string; + transliteration?: string; + }; + ResetPasswordRequest: { + newPassword: string; + token: string; + }; + RoleDefinitionResponse: { + description?: string; + name?: string; + permissions?: string[]; + }; + RowError: { + reason?: string; + /** Format: int32 */ + row?: number; + }; + ScanEventResponse: { + /** Format: uuid */ + id?: string; + /** Format: uuid */ + ticketId?: string; + /** Format: date-time */ + timestamp?: string; + /** Format: uuid */ + zoneId?: string; + zoneName?: string; + }; + ScanRequest: { + /** Format: uuid */ + ticketId: string; + /** Format: uuid */ + zoneId: string; + }; + ScanResult: { + message?: string; + /** @enum {string} */ + outcome?: "SUCCESS" | "INVALID_QR" | "NO_ZONE_ENTITLEMENT" | "ENTRY_LIMIT_REACHED"; + scanEvent?: components["schemas"]["ScanEventResponse"]; + success?: boolean; + }; + SignupRequest: { + /** @enum {string} */ + audience: "brothers" | "sisters" | "both"; + /** Format: email */ + email: string; + firstName: string; + lastName: string; + password: string; + }; + SortObject: { + empty?: boolean; + sorted?: boolean; + unsorted?: boolean; + }; + TicketResponse: { + email?: string; /** Format: uuid */ - targetEntityId?: string; + eventId?: string; + firstName?: string; + holderRef?: string; + /** Format: uuid */ + id?: string; + lastName?: string; + /** Format: date-time */ + lastTicketSent?: string; + ticketType?: components["schemas"]["TicketTypeResponse"]; + }; + TicketTypeResponse: { + /** Format: int32 */ + capacity?: number; + /** Format: date-time */ + createdAt?: string; + entitlements?: components["schemas"]["ZoneEntitlementResponse"][]; + /** Format: uuid */ + eventId?: string; + /** Format: uuid */ + id?: string; + isActive?: boolean; + name?: string; + price?: number; + /** Format: int32 */ + reservedCount?: number; + /** Format: date-time */ + salesEndAt?: string; + /** Format: date-time */ + salesStartAt?: string; + }; + TokenRefreshRequest: { + refreshToken: string; + }; + TokenRefreshResponse: { + accessToken?: string; + refreshToken?: string; + tokenType?: string; + }; + TokenValidationResponse: { + email?: string; + firstName?: string; + /** Format: uuid */ + id?: string; + lastName?: string; + permissions?: string[]; + roles?: string[]; + valid?: boolean; + verified?: boolean; + }; + UpdateBoardConfigRequest: { + /** Format: int32 */ + agendaDurationSeconds?: number; + darkModeAfterIsha?: boolean; + enableScrollingMessage?: boolean; + location?: components["schemas"]["Location"]; + /** Format: int32 */ + nextPrayerDurationSeconds?: number; + scrollingMessages?: string[]; + }; + UpdateCalendarEventRequest: { + allDay?: boolean; + allowUploads?: boolean; /** @enum {string} */ - targetEntityType?: "USER" | "UPLOAD" | "MUSALLAH_BOARD" | "DEVICE" | "EVENT"; + audience?: "brothers" | "sisters" | "both"; + description?: string; /** Format: date-time */ - timestamp?: string; - userAgent?: string; + endTime?: string; + location?: string; + name?: string; + /** Format: date-time */ + startTime?: string; + }; + UpdateDeviceRequest: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + displayName?: string; + }; + UpdateEventRequest: { + description: string; + location: string; + name: string; + /** Format: date-time */ + time: string; + }; + UpdatePosterRequest: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + /** Format: date-time */ + endTime?: string; + signupUrl?: string; + /** Format: date-time */ + startTime?: string; + title?: string; + }; + UpdatePrayerSpaceRequest: { + amenities?: string[]; + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + building?: string; + /** Format: int32 */ + capacity?: number; + clearCoordinates?: boolean; + directions?: string; + entranceDescription?: string; + entranceName?: string; + floor?: string; + imageUrl?: string; + /** Format: double */ + latitude?: number; + /** Format: double */ + longitude?: number; + mapsUrl?: string; + name?: string; + notes?: string; + roomInfo?: string; + startingPoint?: string; + steps?: components["schemas"]["DirectionStepRequest"][]; + tag?: string; + tips?: string[]; + /** @enum {string} */ + type?: "brothers" | "sisters" | "multifaith" | "reflection"; + /** Format: int32 */ + walkTimeMinutes?: number; + }; + UpdateProfileRequest: { + firstName?: string; + lastName?: string; + }; + UpdatePromotableSocialMediaRequest: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + /** Format: int32 */ + duration?: number; + footerText?: string; + handle?: string; + headerText?: string; + heroText?: string; + name?: string; + /** @enum {string} */ + type?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; + url?: string; + }; + UpdateTickerRequest: { + enableScrollingMessage?: boolean; + scrollingMessages?: string[]; + }; + UpdateTicketRequest: { + email?: string; + firstName?: string; + lastName?: string; + /** Format: uuid */ + ticketTypeId?: string; + }; + UpdateTicketTypeRequest: { + entitlements?: components["schemas"]["ZoneEntitlementRequest"][]; + isActive?: boolean; + name: string; + price: number; + /** Format: date-time */ + salesEndAt?: string; + /** Format: date-time */ + salesStartAt?: string; + }; + UpdateZoneRequest: { + description: string; + name: string; + }; + UploadCompletionResponse: { + /** Format: uuid */ + eventId?: string; + /** Format: int64 */ + fileSize?: number; + objectKey?: string; + /** Format: uuid */ + uploadId?: string; + verified?: boolean; + }; + UploadDto: { + anon?: boolean; + approved?: boolean; + /** @enum {string} */ + contentType?: "image" | "video" | "audio" | "document"; + /** Format: date-time */ + createdDate?: string; + /** Format: uuid */ + eventId?: string; + eventName?: string; + featured?: boolean; + fileName?: string; + fileUrl?: string; + instagramHandle?: string; + thumbnailUrl?: string; + uploadDescription?: string; + /** Format: uuid */ + uploadedBy?: string; + /** Format: uuid */ + uuid?: string; + }; + UploadLimitsResponse: { + allowedContentTypes?: string[]; + /** Format: int32 */ + dailyLimit?: number; + /** Format: int64 */ + maxSizeBytes?: number; + /** Format: int64 */ + maxSizeMB?: number; + role?: string; + /** Format: int64 */ + uploadsRemaining?: number; + /** Format: int64 */ + uploadsToday?: number; + /** Format: int32 */ + videoMaxDurationSeconds?: number; + }; + UserInfoResponse: { + /** @enum {string} */ + audience?: "brothers" | "sisters" | "both"; + directPermissions?: string[]; + effectivePermissions?: string[]; + email?: string; + firstName?: string; + /** Format: uuid */ + id?: string; + lastName?: string; + roles?: ("USER" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "TCKET_SCANNER" | "TCKET_MANAGER" | "TCKET_ADMIN" | "ROOT")[]; + verified?: boolean; + }; + ValidateRequest: { + /** Format: uuid */ + ticketId: string; + /** Format: uuid */ + zoneId: string; + }; + ValidateResetTokenRequest: { + token: string; + }; + ValidationResult: { + message?: string; + valid?: boolean; + }; + VerifyEmailRequest: { + token: string; + }; + VerifyUserRequest: { + /** Format: uuid */ + userId: string; + }; + WeeklyContent: { + /** Format: uuid */ + id?: string; + jummahPrayers?: components["schemas"]["JummahPrayer"][]; + quotes?: components["schemas"]["IslamicQuote"][]; + /** Format: int32 */ + weekNumber?: number; + /** Format: int32 */ + year?: number; + }; + WeeklyContentRequest: { + jummahPrayers?: components["schemas"]["JummahSlot"][]; + quotes?: components["schemas"]["QuoteEntry"][]; + }; + WizardEntitlementRequest: { + /** Format: int32 */ + maxEntries?: number; + zoneKey: string; + }; + WizardTicketTypeRequest: { + /** Format: int32 */ + capacity?: number; + entitlements?: components["schemas"]["WizardEntitlementRequest"][]; + isActive?: boolean; + name: string; + price: number; + /** Format: date-time */ + salesEndAt?: string; + /** Format: date-time */ + salesStartAt?: string; + }; + WizardZoneRequest: { + description?: string; + key: string; + name: string; + }; + ZoneEntitlementRequest: { + /** Format: int32 */ + maxEntries?: number; + /** Format: uuid */ + zoneId: string; + }; + ZoneEntitlementResponse: { + /** Format: int32 */ + maxEntries?: number; + /** Format: uuid */ + zoneId?: string; + zoneName?: string; + }; + ZoneResponse: { + description?: string; + /** Format: uuid */ + eventId?: string; + /** Format: uuid */ + id?: string; + name?: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + getAuditEvents: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAuditEventDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getAuditEventsByAction: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path: { + action: "APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "LINK_TICKET_EVENT" | "UNLINK_TICKET_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_PRAYER_SPACE" | "UPDATE_PRAYER_SPACE" | "DELETE_PRAYER_SPACE" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAuditEventDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getAvailableAuditActions: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": ("APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "LINK_TICKET_EVENT" | "UNLINK_TICKET_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_PRAYER_SPACE" | "UPDATE_PRAYER_SPACE" | "DELETE_PRAYER_SPACE" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER")[]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getAuditEventsByDateRange: { + parameters: { + query: { + start: string; + end: string; + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAuditEventDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getFailedOperations: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAuditEventDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getUploadAuditHistory: { + parameters: { + query?: never; + header?: never; + path: { + uploadId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["AuditEventDto"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getAllBoardConfigs: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceConfig"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getAdminBoardConfig: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceConfig"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - BoardEvent: { - allDay?: boolean; - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - description?: string; - /** Format: date-time */ - endTime?: string; - /** Format: uuid */ - id?: string; - location?: string; - name?: string; - /** Format: date-time */ - startTime?: string; + }; + saveBoardConfig: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; }; - ChangePasswordRequest: { - currentPassword: string; - newPassword: string; + requestBody: { + content: { + "application/json": components["schemas"]["DeviceConfig"]; + }; }; - CommandIssuedResponse: { - /** Format: uuid */ - commandId?: string; - /** Format: uuid */ - deviceId?: string; - /** Format: date-time */ - expiresAt?: string; - /** Format: date-time */ - issuedAt?: string; - kind?: string; - /** @enum {string} */ - status?: "PENDING" | "DELIVERED" | "ACKED" | "RUNNING" | "SUCCEEDED" | "FAILED" | "TIMEOUT" | "REJECTED" | "EXPIRED"; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceConfig"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + updateBoardConfig: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateBoardConfigRequest"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceConfig"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + updateBoardTicker: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateTickerRequest"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceConfig"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + listDevices: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceSummary"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + issueEnrollmentToken: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["IssueEnrollmentTokenRequest"]; + }; + }; + responses: { + /** @description Token issued; the plaintext is returned exactly once */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["IssueEnrollmentTokenResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getDevice: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Device found */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceSummary"]; + }; + }; + /** @description No device with that id */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + updateDevice: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; }; - CommandView: { - /** Format: date-time */ - ackedAt?: string; - /** Format: int32 */ - deadlineMs?: number; - /** Format: date-time */ - deliveredAt?: string; - /** Format: uuid */ - deviceId?: string; - errorMessage?: string; - /** Format: date-time */ - expiresAt?: string; - /** Format: date-time */ - finishedAt?: string; - /** Format: uuid */ - id?: string; - /** Format: date-time */ - issuedAt?: string; - issuedBy?: string; - kind?: string; - output?: components["schemas"]["JsonNode"]; - payload?: components["schemas"]["JsonNode"]; - /** Format: date-time */ - startedAt?: string; - /** @enum {string} */ - status?: "PENDING" | "DELIVERED" | "ACKED" | "RUNNING" | "SUCCEEDED" | "FAILED" | "TIMEOUT" | "REJECTED" | "EXPIRED"; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateDeviceRequest"]; + }; }; - CreateCalendarEventRequest: { - allDay?: boolean; - /** @enum {string} */ - audience: "brothers" | "sisters" | "both"; - description: string; - /** Format: int64 */ - endEpochMs: number; - location: string; - name: string; - /** Format: int64 */ - startEpochMs: number; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceSummary"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - CreatePosterRequest: { - /** @enum {string} */ - audience: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - /** Format: date-time */ - endTime: string; - /** Format: binary */ - imageFile: string; - signupUrl?: string; - /** Format: date-time */ - startTime: string; - title: string; + }; + listDeviceCommands: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; }; - CreatePromotableSocialMediaRequest: { - /** @enum {string} */ - audience: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - footerText: string; - handle?: string; - headerText: string; - heroText: string; - name: string; - /** @enum {string} */ - type: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; - url: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CommandView"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - CreateUserRequest: { - /** Format: email */ - email: string; - firstName: string; - lastName: string; - /** @description Optional. Omit it to create a disabled account and email the user a password reset link; supply it to create an account that can sign in right away. */ - password?: string; - studentNumber: string; + }; + issueDeviceCommand: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; }; - DayBucket: { - /** Format: date */ - date?: string; - events?: components["schemas"]["EventView"][]; + requestBody: { + content: { + "application/json": components["schemas"]["IssueCommandRequest"]; + }; }; - DeviceConfig: { - /** Format: int32 */ - agendaDurationSeconds?: number; - darkModeAfterIsha?: boolean; - enableScrollingMessage?: boolean; - /** Format: uuid */ - id?: string; - location?: components["schemas"]["Location"]; - /** Format: int32 */ - nextPrayerDurationSeconds?: number; - scrollingMessages?: string[]; + responses: { + /** @description Command accepted and queued for delivery */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["CommandIssuedResponse"]; + }; + }; + /** @description Caller lacks the permission for this command kind */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - DeviceSummary: { - agentVersion?: string; - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - displayName?: string; - /** Format: date-time */ - enrolledAt?: string; - hardwareModel?: string; - /** Format: uuid */ - id?: string; - /** Format: date-time */ - lastHeartbeat?: string; - lastSeenIp?: string; - /** Format: date-time */ - revokedAt?: string; + }; + revokeDevice: { + parameters: { + query?: never; + header?: never; + path: { + deviceId: string; + }; + cookie?: never; }; - EventView: { - allDay?: boolean; - description?: string; - /** Format: date-time */ - endTime?: string; - location?: string; - name?: string; - /** Format: date-time */ - startTime?: string; + requestBody?: never; + responses: { + /** @description Device revoked */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["DeviceSummary"]; + }; + }; + /** @description No device with that id */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - FrameConfig: { - type: string; + }; + getBoardEvents: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - FrameDefinition: { - /** Format: int32 */ - durationInSeconds?: number; - frameConfig?: components["schemas"]["AgendaFrameConfig"] | components["schemas"]["IslamicQuoteFrameConfig"] | components["schemas"]["JummahFrameConfig"] | components["schemas"]["NextPrayerFrameConfig"] | components["schemas"]["PosterFrameConfig"] | components["schemas"]["PromotableSocialMediaFrameConfig"]; - frameId?: string; - /** @enum {string} */ - frameType?: "poster" | "next_prayer" | "agenda" | "jummah" | "islamic_quote" | "socials"; + }; + createBoardEvent: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - GalleryItemDto: { - author?: string; - date?: string; - event?: string; - featured?: boolean; - id?: string; - src?: string; - thumbnail?: string; - title?: string; - type?: string; + requestBody: { + content: { + "application/json": components["schemas"]["CreateCalendarEventRequest"]; + }; }; - IslamicQuote: { - arabic?: string; - /** Format: int32 */ - durationSeconds?: number; - /** Format: uuid */ - id?: string; - /** @enum {string} */ - kind?: "VERSE" | "HADITH"; - reference?: string; - translation?: string; - transliteration?: string; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - IslamicQuoteFrameConfig: Omit & { - arabic?: string; - /** @enum {string} */ - kind?: "VERSE" | "HADITH"; - reference?: string; - translation?: string; - transliteration?: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "islamic_quote"; + }; + getEventsForAudience: { + parameters: { + query: { + audience: "brothers" | "sisters" | "both"; + }; + header?: never; + path?: never; + cookie?: never; }; - IssueCommandRequest: { - /** Format: int32 */ - deadlineMs?: number; - kind: string; - payload?: components["schemas"]["JsonNode"]; - /** Format: int32 */ - ttlSeconds?: number; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - IssueEnrollmentTokenRequest: { - /** @enum {string} */ - audience: "brothers" | "sisters" | "both"; - displayName: string; - /** Format: int32 */ - expiresInMinutes?: number; + }; + getBoardEventById: { + parameters: { + query?: never; + header?: never; + path: { + eventId: string; + }; + cookie?: never; }; - IssueEnrollmentTokenResponse: { - /** Format: date-time */ - expiresAt?: string; - token?: string; - /** Format: uuid */ - tokenId?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - JsonNode: unknown; - JummahFrameConfig: Omit & { - prayers?: components["schemas"]["JummahSlot"][]; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "jummah"; + }; + deleteEvent_1: { + parameters: { + query?: never; + header?: never; + path: { + eventId: string; + }; + cookie?: never; }; - JummahPrayer: { - /** Format: uuid */ - id?: string; - khatib?: string; - prayerTime?: string; - room?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - JummahSlot: { - khatib?: string; - prayerTime?: string; - room?: string; + }; + updateEvent_1: { + parameters: { + query?: never; + header?: never; + path: { + eventId: string; + }; + cookie?: never; }; - JwtResponse: { - email?: string; - firstName?: string; - /** Format: uuid */ - id?: string; - lastName?: string; - permissions?: string[]; - refreshToken?: string; - roles?: string[]; - token?: string; - type?: string; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateCalendarEventRequest"]; + }; }; - Location: { - city?: string; - country?: string; - /** Format: double */ - latitude?: number; - /** Format: double */ - longitude?: number; - /** @enum {string} */ - method?: "KARACHI" | "ISNA" | "MWL" | "MAKKAH" | "EGYPT" | "TEHRAN" | "GULF" | "KUWAIT" | "QATAR" | "SINGAPORE" | "FRANCE" | "TURKEY" | "RUSSIA" | "DUBAI"; - timezone?: string; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - LoginRequest: { - /** Format: email */ - email: string; - password: string; + }; + unlinkTicketEvent: { + parameters: { + query?: never; + header?: never; + path: { + eventId: string; + }; + cookie?: never; }; - MediaEvent: { - /** Format: date-time */ - date?: string; - /** Format: uuid */ - id?: string; - name?: string; - /** @enum {string} */ - status?: "UPCOMING" | "ONGOING" | "PAST"; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - MessageResponse: { - message?: string; + }; + linkTicketEvent: { + parameters: { + query?: never; + header?: never; + path: { + eventId: string; + tcketEventId: string; + }; + cookie?: never; }; - MusallahBoardPayload: { - deviceConfig?: components["schemas"]["DeviceConfig"]; - frames?: components["schemas"]["FrameDefinition"][]; - weather?: components["schemas"]["JsonNode"]; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BoardEvent"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - NextPrayerFrameConfig: Omit & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "next_prayer"; + }; + getAllPosters: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - PageAdminUploadDto: { - content?: components["schemas"]["AdminUploadDto"][]; - empty?: boolean; - first?: boolean; - last?: boolean; - /** Format: int32 */ - number?: number; - /** Format: int32 */ - numberOfElements?: number; - pageable?: components["schemas"]["Pageable"]; - /** Format: int32 */ - size?: number; - sort?: components["schemas"]["SortObject"]; - /** Format: int64 */ - totalElements?: number; - /** Format: int32 */ - totalPages?: number; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - PageAuditEventDto: { - content?: components["schemas"]["AuditEventDto"][]; - empty?: boolean; - first?: boolean; - last?: boolean; - /** Format: int32 */ - number?: number; - /** Format: int32 */ - numberOfElements?: number; - pageable?: components["schemas"]["Pageable"]; - /** Format: int32 */ - size?: number; - sort?: components["schemas"]["SortObject"]; - /** Format: int64 */ - totalElements?: number; - /** Format: int32 */ - totalPages?: number; + }; + createPoster: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - PageGalleryItemDto: { - content?: components["schemas"]["GalleryItemDto"][]; - empty?: boolean; - first?: boolean; - last?: boolean; - /** Format: int32 */ - number?: number; - /** Format: int32 */ - numberOfElements?: number; - pageable?: components["schemas"]["Pageable"]; - /** Format: int32 */ - size?: number; - sort?: components["schemas"]["SortObject"]; - /** Format: int64 */ - totalElements?: number; - /** Format: int32 */ - totalPages?: number; + requestBody?: { + content: { + "multipart/form-data": components["schemas"]["CreatePosterRequest"]; + }; }; - PageUploadDto: { - content?: components["schemas"]["UploadDto"][]; - empty?: boolean; - first?: boolean; - last?: boolean; - /** Format: int32 */ - number?: number; - /** Format: int32 */ - numberOfElements?: number; - pageable?: components["schemas"]["Pageable"]; - /** Format: int32 */ - size?: number; - sort?: components["schemas"]["SortObject"]; - /** Format: int64 */ - totalElements?: number; - /** Format: int32 */ - totalPages?: number; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - PageUserInfoResponse: { - content?: components["schemas"]["UserInfoResponse"][]; - empty?: boolean; - first?: boolean; - last?: boolean; - /** Format: int32 */ - number?: number; - /** Format: int32 */ - numberOfElements?: number; - pageable?: components["schemas"]["Pageable"]; - /** Format: int32 */ - size?: number; - sort?: components["schemas"]["SortObject"]; - /** Format: int64 */ - totalElements?: number; - /** Format: int32 */ - totalPages?: number; + }; + getPostersForAudience: { + parameters: { + query: { + audience: "brothers" | "sisters" | "both"; + }; + header?: never; + path?: never; + cookie?: never; }; - Pageable: { - /** Format: int32 */ - page?: number; - /** Format: int32 */ - size?: number; - sort?: string[]; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + getPosterById: { + parameters: { + query?: never; + header?: never; + path: { + posterId: string; + }; + cookie?: never; }; - PermissionResponse: { - authority?: string; - domain?: string; - name?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - Poster: { - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - /** Format: date-time */ - endTime?: string; - /** Format: uuid */ - id?: string; - image?: string; - signupUrl?: string; - /** Format: date-time */ - startTime?: string; - title?: string; + }; + deletePoster: { + parameters: { + query?: never; + header?: never; + path: { + posterId: string; + }; + cookie?: never; }; - PosterFrameConfig: Omit & { - posterUrl?: string; - signupUrl?: string; - title?: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "poster"; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - PresignedUploadResponse: { - contentType?: string; - /** Format: uuid */ - eventId?: string; - expectedSha256?: string; - /** Format: int32 */ - expiresInMinutes?: number; - method?: string; - objectKey?: string; - uploadUrl?: string; + }; + updatePoster: { + parameters: { + query?: never; + header?: never; + path: { + posterId: string; + }; + cookie?: never; }; - PromotableSocialMedia: { - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - footerText?: string; - handle?: string; - headerText?: string; - heroText?: string; - /** Format: uuid */ - id?: string; - name?: string; - /** @enum {string} */ - type?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; - url?: string; + requestBody: { + content: { + "application/json": components["schemas"]["UpdatePosterRequest"]; + }; }; - PromotableSocialMediaFrameConfig: Omit & { - footerText?: string; - handle?: string; - headerText?: string; - heroText?: string; - /** @enum {string} */ - socialType?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; - url?: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "socials"; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - QuoteEntry: { - arabic?: string; - /** Format: int32 */ - durationSeconds?: number; - /** @enum {string} */ - kind?: "VERSE" | "HADITH"; - reference?: string; - translation?: string; - transliteration?: string; + }; + updatePosterImage: { + parameters: { + query?: never; + header?: never; + path: { + posterId: string; + }; + cookie?: never; }; - ResetPasswordRequest: { - newPassword: string; - token: string; + requestBody?: { + content: { + "multipart/form-data": { + /** Format: binary */ + image: string; + }; + }; }; - RoleDefinitionResponse: { - description?: string; - name?: string; - permissions?: string[]; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Poster"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - SignupRequest: { - /** Format: email */ - email: string; - firstName: string; - lastName: string; - password: string; - studentNumber: string; + }; + refreshSignboard: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - SortObject: { - empty?: boolean; - sorted?: boolean; - unsorted?: boolean; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - TokenRefreshRequest: { - refreshToken: string; + }; + getAllSocials: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - TokenRefreshResponse: { - accessToken?: string; - refreshToken?: string; - tokenType?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PromotableSocialMedia"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - TokenValidationResponse: { - email?: string; - firstName?: string; - /** Format: uuid */ - id?: string; - lastName?: string; - permissions?: string[]; - roles?: string[]; - valid?: boolean; - verified?: boolean; + }; + createSocial: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - UpdateBoardConfigRequest: { - /** Format: int32 */ - agendaDurationSeconds?: number; - darkModeAfterIsha?: boolean; - enableScrollingMessage?: boolean; - location?: components["schemas"]["Location"]; - /** Format: int32 */ - nextPrayerDurationSeconds?: number; - scrollingMessages?: string[]; + requestBody: { + content: { + "application/json": components["schemas"]["CreatePromotableSocialMediaRequest"]; + }; }; - UpdateCalendarEventRequest: { - allDay?: boolean; - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - description?: string; - /** Format: date-time */ - endTime?: string; - location?: string; - name?: string; - /** Format: date-time */ - startTime?: string; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PromotableSocialMedia"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - UpdateDeviceRequest: { - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - displayName?: string; + }; + getSocialsForAudience: { + parameters: { + query: { + audience: "brothers" | "sisters" | "both"; + }; + header?: never; + path?: never; + cookie?: never; }; - UpdatePosterRequest: { - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - /** Format: date-time */ - endTime?: string; - signupUrl?: string; - /** Format: date-time */ - startTime?: string; - title?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PromotableSocialMedia"][]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - UpdateProfileRequest: { - firstName?: string; - lastName?: string; - studentNumber?: string; + }; + getSocialById: { + parameters: { + query?: never; + header?: never; + path: { + socialId: string; + }; + cookie?: never; }; - UpdatePromotableSocialMediaRequest: { - /** @enum {string} */ - audience?: "brothers" | "sisters" | "both"; - /** Format: int32 */ - duration?: number; - footerText?: string; - handle?: string; - headerText?: string; - heroText?: string; - name?: string; - /** @enum {string} */ - type?: "instagram" | "youtube" | "tiktok" | "whatsapp" | "other"; - url?: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PromotableSocialMedia"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - UpdateTickerRequest: { - enableScrollingMessage?: boolean; - scrollingMessages?: string[]; + }; + deleteSocial: { + parameters: { + query?: never; + header?: never; + path: { + socialId: string; + }; + cookie?: never; }; - UploadCompletionResponse: { - /** Format: uuid */ - eventId?: string; - /** Format: int64 */ - fileSize?: number; - objectKey?: string; - /** Format: uuid */ - uploadId?: string; - verified?: boolean; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - UploadDto: { - anon?: boolean; - approved?: boolean; - /** @enum {string} */ - contentType?: "image" | "video" | "audio" | "document"; - /** Format: date-time */ - createdDate?: string; - /** Format: uuid */ - eventId?: string; - eventName?: string; - featured?: boolean; - fileName?: string; - fileUrl?: string; - instagramHandle?: string; - thumbnailUrl?: string; - uploadDescription?: string; - /** Format: uuid */ - uploadedBy?: string; - /** Format: uuid */ - uuid?: string; + }; + updateSocial: { + parameters: { + query?: never; + header?: never; + path: { + socialId: string; + }; + cookie?: never; }; - UploadLimitsResponse: { - allowedContentTypes?: string[]; - /** Format: int32 */ - dailyLimit?: number; - /** Format: int64 */ - maxSizeBytes?: number; - /** Format: int64 */ - maxSizeMB?: number; - role?: string; - /** Format: int64 */ - uploadsRemaining?: number; - /** Format: int64 */ - uploadsToday?: number; - /** Format: int32 */ - videoMaxDurationSeconds?: number; + requestBody: { + content: { + "application/json": components["schemas"]["UpdatePromotableSocialMediaRequest"]; + }; }; - UserInfoResponse: { - directPermissions?: string[]; - effectivePermissions?: string[]; - email?: string; - firstName?: string; - /** Format: uuid */ - id?: string; - lastName?: string; - roles?: ("USER" | "ADMIN" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "ROOT")[]; - studentNumber?: string; - verified?: boolean; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PromotableSocialMedia"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - UserStatsResponse: { - /** Format: int32 */ - approvedUploads?: number; - /** Format: int32 */ - featuredUploads?: number; - /** Format: int32 */ - pendingUploads?: number; - /** Format: int32 */ - totalUploads?: number; + }; + getAdminUploads: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; }; - ValidateResetTokenRequest: { - token: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAdminUploadDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - VerifyEmailRequest: { - token: string; + }; + getApprovedUploads: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; }; - VerifyUserRequest: { - /** Format: uuid */ - userId: string; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAdminUploadDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; - WeeklyContent: { - /** Format: uuid */ - id?: string; - jummahPrayers?: components["schemas"]["JummahPrayer"][]; - quotes?: components["schemas"]["IslamicQuote"][]; - /** Format: int32 */ - weekNumber?: number; - /** Format: int32 */ - year?: number; + }; + getFeaturedUploads: { + parameters: { + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; + header?: never; + path?: never; + cookie?: never; }; - WeeklyContentRequest: { - jummahPrayers?: components["schemas"]["JummahSlot"][]; - quotes?: components["schemas"]["QuoteEntry"][]; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAdminUploadDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; }; }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { - getAuditEvents: { + getPendingUploads: { parameters: { query?: { /** @description Zero-based page index (0..N) */ @@ -2083,7 +4617,100 @@ export interface operations { sort?: string[]; }; header?: never; - path?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PageAdminUploadDto"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + deleteUpload: { + parameters: { + query?: never; + header?: never; + path: { + uploadId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + unapproveUpload: { + parameters: { + query?: never; + header?: never; + path: { + uploadId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + approveUpload: { + parameters: { + query?: never; + header?: never; + path: { + uploadId: string; + }; cookie?: never; }; requestBody?: never; @@ -2094,7 +4721,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAuditEventDto"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2108,19 +4735,12 @@ export interface operations { }; }; }; - getAuditEventsByAction: { + featureUpload: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; path: { - action: "APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER"; + uploadId: string; }; cookie?: never; }; @@ -2132,7 +4752,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAuditEventDto"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2146,11 +4766,13 @@ export interface operations { }; }; }; - getAvailableAuditActions: { + unfeatureUpload: { parameters: { query?: never; header?: never; - path?: never; + path: { + uploadId: string; + }; cookie?: never; }; requestBody?: never; @@ -2161,7 +4783,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": ("APPROVE_UPLOAD" | "UNAPPROVE_UPLOAD" | "DELETE_UPLOAD" | "FEATURE_UPLOAD" | "UNFEATURE_UPLOAD" | "CREATE_EVENT" | "UPDATE_EVENT" | "DELETE_EVENT" | "CREATE_CALENDAR_EVENT" | "UPDATE_CALENDAR_EVENT" | "DELETE_CALENDAR_EVENT" | "CREATE_POSTER" | "UPDATE_POSTER" | "DELETE_POSTER" | "CREATE_SOCIAL" | "UPDATE_SOCIAL" | "DELETE_SOCIAL" | "SAVE_WEEKLY_CONTENT" | "DELETE_WEEKLY_CONTENT" | "UPDATE_BOARD_CONFIG" | "UPDATE_BOARD_TICKER" | "REFRESH_BOARDS" | "ISSUE_ENROLLMENT_TOKEN" | "REVOKE_DEVICE" | "ISSUE_DEVICE_COMMAND" | "PROMOTE_USER" | "DEMOTE_USER" | "DISABLE_USER" | "ENABLE_USER" | "VIEW_AUDIT_LOGS" | "EXPORT_DATA" | "SYSTEM_MAINTENANCE" | "VERIFY_USER" | "UNVERIFY_USER" | "RESET_USER_PASSWORD" | "TRIGGER_PASSWORD_RESET_EMAIL" | "ADD_USER_ROLE" | "REMOVE_USER_ROLE" | "GRANT_PERMISSION" | "REVOKE_PERMISSION" | "ADD_USER" | "REMOVE_USER" | "UPDATE_USER")[]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2175,18 +4797,9 @@ export interface operations { }; }; }; - getAuditEventsByDateRange: { + getAllWeeklyContent: { parameters: { - query: { - start: string; - end: string; - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; path?: never; cookie?: never; @@ -2199,7 +4812,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAuditEventDto"]; + "application/json": components["schemas"]["WeeklyContent"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2213,18 +4826,13 @@ export interface operations { }; }; }; - getFailedOperations: { + getWeeklyContentByYear: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; - path?: never; + path: { + year: number; + }; cookie?: never; }; requestBody?: never; @@ -2235,7 +4843,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAuditEventDto"]; + "application/json": components["schemas"]["WeeklyContent"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2249,12 +4857,13 @@ export interface operations { }; }; }; - getUploadAuditHistory: { + getAdminWeeklyContent: { parameters: { query?: never; header?: never; path: { - uploadId: string; + year: number; + weekNumber: number; }; cookie?: never; }; @@ -2266,7 +4875,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["AuditEventDto"][]; + "application/json": components["schemas"]["WeeklyContent"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2280,14 +4889,21 @@ export interface operations { }; }; }; - getAllBoardConfigs: { + saveWeeklyContent: { parameters: { query?: never; header?: never; - path?: never; + path: { + year: number; + weekNumber: number; + }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["WeeklyContentRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -2295,7 +4911,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"][]; + "application/json": components["schemas"]["WeeklyContent"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2309,12 +4925,13 @@ export interface operations { }; }; }; - getAdminBoardConfig: { + deleteWeeklyContent: { parameters: { query?: never; header?: never; path: { - deviceId: string; + year: number; + weekNumber: number; }; cookie?: never; }; @@ -2326,7 +4943,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2340,20 +4957,14 @@ export interface operations { }; }; }; - saveBoardConfig: { + listAdminPrayerSpaces: { parameters: { query?: never; header?: never; - path: { - deviceId: string; - }; + path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["DeviceConfig"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -2361,7 +4972,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"]; + "application/json": components["schemas"]["PrayerSpaceView"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2375,18 +4986,16 @@ export interface operations { }; }; }; - updateBoardConfig: { + createPrayerSpace: { parameters: { query?: never; header?: never; - path: { - deviceId: string; - }; + path?: never; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["UpdateBoardConfigRequest"]; + "application/json": components["schemas"]["CreatePrayerSpaceRequest"]; }; }; responses: { @@ -2396,7 +5005,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"]; + "application/json": components["schemas"]["PrayerSpaceView"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2410,20 +5019,16 @@ export interface operations { }; }; }; - updateBoardTicker: { + getAdminPrayerSpace: { parameters: { query?: never; header?: never; path: { - deviceId: string; + id: string; }; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdateTickerRequest"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -2431,7 +5036,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"]; + "application/json": components["schemas"]["PrayerSpaceView"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2445,11 +5050,13 @@ export interface operations { }; }; }; - listDevices: { + deletePrayerSpace: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -2460,7 +5067,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceSummary"][]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2474,26 +5081,28 @@ export interface operations { }; }; }; - issueEnrollmentToken: { + updatePrayerSpace: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["IssueEnrollmentTokenRequest"]; + "application/json": components["schemas"]["UpdatePrayerSpaceRequest"]; }; }; responses: { - /** @description Token issued; the plaintext is returned exactly once */ - 201: { + /** @description OK */ + 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["IssueEnrollmentTokenResponse"]; + "application/json": components["schemas"]["PrayerSpaceView"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2507,28 +5116,26 @@ export interface operations { }; }; }; - getDevice: { + getAvailablePermissions: { parameters: { query?: never; header?: never; - path: { - deviceId: string; - }; + path?: never; cookie?: never; }; requestBody?: never; responses: { - /** @description Device found */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceSummary"]; + "application/json": components["schemas"]["PermissionResponse"][]; }; }; - /** @description No device with that id */ - 404: { + /** @description Request failed; body carries a human-readable message */ + default: { headers: { [name: string]: unknown; }; @@ -2536,6 +5143,26 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; + }; + }; + getAvailableRoles: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RoleDefinitionResponse"][]; + }; + }; /** @description Request failed; body carries a human-readable message */ default: { headers: { @@ -2547,18 +5174,49 @@ export interface operations { }; }; }; - updateDevice: { + createUser: { parameters: { query?: never; header?: never; - path: { - deviceId: string; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["CreateUserRequest"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; }; + }; + }; + verifyUser: { + parameters: { + query?: never; + header?: never; + path?: never; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["UpdateDeviceRequest"]; + "application/json": components["schemas"]["VerifyUserRequest"]; }; }; responses: { @@ -2568,7 +5226,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceSummary"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2582,16 +5240,20 @@ export interface operations { }; }; }; - listDeviceCommands: { + addRoleToUser: { parameters: { query?: never; header?: never; path: { - deviceId: string; + userId: string; }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": "USER" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "TCKET_SCANNER" | "TCKET_MANAGER" | "TCKET_ADMIN" | "ROOT"; + }; + }; responses: { /** @description OK */ 200: { @@ -2599,7 +5261,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["CommandView"][]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2613,32 +5275,58 @@ export interface operations { }; }; }; - issueDeviceCommand: { + grantUserPermission: { parameters: { query?: never; header?: never; path: { - deviceId: string; + userId: string; }; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["IssueCommandRequest"]; + "application/json": "MEDIA_UPLOAD_SELF" | "MEDIA_UPLOAD_MODERATE" | "MEDIA_UPLOAD_READ" | "BOARD_CONTENT_READ" | "BOARD_POSTER_WRITE" | "BOARD_EVENT_WRITE" | "BOARD_SOCIAL_WRITE" | "BOARD_WEEKLY_WRITE" | "BOARD_TICKER_WRITE" | "BOARD_PRAYER_SPACE_WRITE" | "BOARD_CONFIG_READ" | "BOARD_CONFIG_WRITE" | "BOARD_REFRESH" | "BOARD_DEVICE_READ" | "BOARD_DEVICE_ENROLL" | "BOARD_DEVICE_REVOKE" | "BOARD_TELEMETRY_SUBSCRIBE" | "BOARD_COMMAND_BENIGN" | "BOARD_COMMAND_DISRUPTIVE" | "BOARD_COMMAND_INSPECT" | "TCKET_SCAN" | "TCKET_MANAGE" | "TCKET_ADMIN" | "IAM_USER_READ" | "IAM_USER_WRITE" | "IAM_ROLE_GRANT" | "AUDIT_READ"; }; }; responses: { - /** @description Command accepted and queued for delivery */ - 202: { + /** @description OK */ + 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["CommandIssuedResponse"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; - /** @description Caller lacks the permission for this command kind */ - 403: { + /** @description Request failed; body carries a human-readable message */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + }; + }; + removeRoleFromUser: { + parameters: { + query?: never; + header?: never; + path: { + userId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": "USER" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "TCKET_SCANNER" | "TCKET_MANAGER" | "TCKET_ADMIN" | "ROOT"; + }; + }; + responses: { + /** @description OK */ + 200: { headers: { [name: string]: unknown; }; @@ -2657,28 +5345,23 @@ export interface operations { }; }; }; - revokeDevice: { + revokeUserPermission: { parameters: { query?: never; header?: never; path: { - deviceId: string; + userId: string; }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": "MEDIA_UPLOAD_SELF" | "MEDIA_UPLOAD_MODERATE" | "MEDIA_UPLOAD_READ" | "BOARD_CONTENT_READ" | "BOARD_POSTER_WRITE" | "BOARD_EVENT_WRITE" | "BOARD_SOCIAL_WRITE" | "BOARD_WEEKLY_WRITE" | "BOARD_TICKER_WRITE" | "BOARD_PRAYER_SPACE_WRITE" | "BOARD_CONFIG_READ" | "BOARD_CONFIG_WRITE" | "BOARD_REFRESH" | "BOARD_DEVICE_READ" | "BOARD_DEVICE_ENROLL" | "BOARD_DEVICE_REVOKE" | "BOARD_TELEMETRY_SUBSCRIBE" | "BOARD_COMMAND_BENIGN" | "BOARD_COMMAND_DISRUPTIVE" | "BOARD_COMMAND_INSPECT" | "TCKET_SCAN" | "TCKET_MANAGE" | "TCKET_ADMIN" | "IAM_USER_READ" | "IAM_USER_WRITE" | "IAM_ROLE_GRANT" | "AUDIT_READ"; + }; + }; responses: { - /** @description Device revoked */ + /** @description OK */ 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["DeviceSummary"]; - }; - }; - /** @description No device with that id */ - 404: { headers: { [name: string]: unknown; }; @@ -2697,9 +5380,16 @@ export interface operations { }; }; }; - getBoardEvents: { + getAllUsers: { parameters: { - query?: never; + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; + }; header?: never; path?: never; cookie?: never; @@ -2712,7 +5402,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"][]; + "application/json": components["schemas"]["PageUserInfoResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2726,7 +5416,7 @@ export interface operations { }; }; }; - createBoardEvent: { + enrollAgent: { parameters: { query?: never; header?: never; @@ -2735,21 +5425,21 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["CreateCalendarEventRequest"]; + "application/json": components["schemas"]["AgentEnrollRequest"]; }; }; responses: { - /** @description OK */ + /** @description Device enrolled; identity and websocket URL returned */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"]; + "application/json": components["schemas"]["AgentEnrollResponse"]; }; }; - /** @description Request failed; body carries a human-readable message */ - default: { + /** @description Public key malformed or unsupported */ + 400: { headers: { [name: string]: unknown; }; @@ -2757,26 +5447,13 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; - }; - }; - getEventsForAudience: { - parameters: { - query: { - audience: "brothers" | "sisters" | "both"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { + /** @description Enrollment token invalid, expired, or already used */ + 401: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"][]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2790,24 +5467,44 @@ export interface operations { }; }; }; - getBoardEventById: { + changePassword: { parameters: { query?: never; header?: never; - path: { - eventId: string; - }; + path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["ChangePasswordRequest"]; + }; + }; responses: { - /** @description OK */ + /** @description Password changed; all sessions revoked */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Current password incorrect or new password rejected */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Not signed in */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2821,18 +5518,18 @@ export interface operations { }; }; }; - deleteEvent: { + requestPasswordReset: { parameters: { - query?: never; - header?: never; - path: { - eventId: string; + query: { + email: string; }; + header?: never; + path?: never; cookie?: never; }; requestBody?: never; responses: { - /** @description OK */ + /** @description Request accepted */ 200: { headers: { [name: string]: unknown; @@ -2841,6 +5538,15 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; + /** @description Email missing */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; + }; + }; /** @description Request failed; body carries a human-readable message */ default: { headers: { @@ -2852,28 +5558,26 @@ export interface operations { }; }; }; - updateEvent: { + logout: { parameters: { query?: never; header?: never; - path: { - eventId: string; - }; + path?: never; cookie?: never; }; - requestBody: { + requestBody?: { content: { - "application/json": components["schemas"]["UpdateCalendarEventRequest"]; + "application/json": components["schemas"]["TokenRefreshRequest"]; }; }; responses: { - /** @description OK */ + /** @description Session ended */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"]; + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2887,7 +5591,7 @@ export interface operations { }; }; }; - getAllPosters: { + logoutAllDevices: { parameters: { query?: never; header?: never; @@ -2896,13 +5600,22 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description All sessions ended */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"][]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Not signed in */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2916,26 +5629,35 @@ export interface operations { }; }; }; - createPoster: { + refreshToken: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: { + requestBody: { content: { - "multipart/form-data": components["schemas"]["CreatePosterRequest"]; + "application/json": components["schemas"]["TokenRefreshRequest"]; }; }; responses: { - /** @description OK */ + /** @description New access and refresh tokens issued */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"]; + "application/json": components["schemas"]["TokenRefreshResponse"]; + }; + }; + /** @description Refresh token unknown, expired or already revoked */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2949,24 +5671,35 @@ export interface operations { }; }; }; - getPostersForAudience: { + resetPassword: { parameters: { - query: { - audience: "brothers" | "sisters" | "both"; - }; + query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["ResetPasswordRequest"]; + }; + }; responses: { - /** @description OK */ + /** @description Password reset */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"][]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Token or password missing, invalid or expired */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -2980,28 +5713,30 @@ export interface operations { }; }; }; - getPosterById: { + signIn: { parameters: { query?: never; header?: never; - path: { - posterId: string; - }; + path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["LoginRequest"]; + }; + }; responses: { - /** @description OK */ + /** @description Authenticated; access and refresh tokens issued */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"]; + "application/json": components["schemas"]["JwtResponse"]; }; }; - /** @description Request failed; body carries a human-readable message */ - default: { + /** @description Bad credentials or disabled account */ + 401: { headers: { [name: string]: unknown; }; @@ -3009,21 +5744,8 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; - }; - }; - deletePoster: { - parameters: { - query?: never; - header?: never; - path: { - posterId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { + /** @description Too many failed attempts; account temporarily locked */ + 429: { headers: { [name: string]: unknown; }; @@ -3042,28 +5764,35 @@ export interface operations { }; }; }; - updatePoster: { + signUp: { parameters: { query?: never; header?: never; - path: { - posterId: string; - }; + path?: never; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["UpdatePosterRequest"]; + "application/json": components["schemas"]["SignupRequest"]; }; }; responses: { - /** @description OK */ + /** @description Account created; verification email sent */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Validation failed or email already registered */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3077,31 +5806,35 @@ export interface operations { }; }; }; - updatePosterImage: { + validateResetToken: { parameters: { query?: never; header?: never; - path: { - posterId: string; - }; + path?: never; cookie?: never; }; - requestBody?: { + requestBody: { content: { - "multipart/form-data": { - /** Format: binary */ - image: string; - }; + "application/json": components["schemas"]["ValidateResetTokenRequest"]; }; }; responses: { - /** @description OK */ + /** @description Token is valid */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Poster"]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Token missing, invalid or expired */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3115,7 +5848,7 @@ export interface operations { }; }; }; - refreshSignboard: { + validateToken: { parameters: { query?: never; header?: never; @@ -3124,8 +5857,17 @@ export interface operations { }; requestBody?: never; responses: { - /** @description OK */ + /** @description Token valid; user returned */ 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TokenValidationResponse"]; + }; + }; + /** @description Token missing, expired, or user no longer exists */ + 401: { headers: { [name: string]: unknown; }; @@ -3144,22 +5886,35 @@ export interface operations { }; }; }; - getAllSocials: { + verifyEmail: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["VerifyEmailRequest"]; + }; + }; responses: { - /** @description OK */ + /** @description Email verified */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PromotableSocialMedia"][]; + "application/json": components["schemas"]["MessageResponse"]; + }; + }; + /** @description Missing, invalid or expired token */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MessageResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3173,18 +5928,18 @@ export interface operations { }; }; }; - createSocial: { + getMinbarEvents: { parameters: { - query?: never; + query: { + audience: "brothers" | "sisters" | "both"; + year: number; + month: number; + }; header?: never; path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["CreatePromotableSocialMediaRequest"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -3192,7 +5947,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PromotableSocialMedia"]; + "application/json": components["schemas"]["EventView"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3206,7 +5961,7 @@ export interface operations { }; }; }; - getSocialsForAudience: { + getMinbarPrayerSpaces: { parameters: { query: { audience: "brothers" | "sisters" | "both"; @@ -3223,7 +5978,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PromotableSocialMedia"][]; + "application/json": components["schemas"]["PrayerSpaceView"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3237,12 +5992,12 @@ export interface operations { }; }; }; - getSocialById: { + getMinbarPrayerSpaceById: { parameters: { query?: never; header?: never; path: { - socialId: string; + id: string; }; cookie?: never; }; @@ -3254,7 +6009,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PromotableSocialMedia"]; + "application/json": components["schemas"]["PrayerSpaceView"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3268,13 +6023,13 @@ export interface operations { }; }; }; - deleteSocial: { + getPublicBoardConfig: { parameters: { - query?: never; - header?: never; - path: { - socialId: string; + query: { + deviceId: string; }; + header?: never; + path?: never; cookie?: never; }; requestBody?: never; @@ -3285,7 +6040,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["DeviceConfig"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3299,20 +6054,16 @@ export interface operations { }; }; }; - updateSocial: { + getUpcomingEvents: { parameters: { - query?: never; - header?: never; - path: { - socialId: string; + query: { + audience: "brothers" | "sisters" | "both"; }; + header?: never; + path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdatePromotableSocialMediaRequest"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -3320,7 +6071,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PromotableSocialMedia"]; + "application/json": components["schemas"]["BoardEvent"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3334,9 +6085,13 @@ export interface operations { }; }; }; - getAllWeeklyContent: { + getEventsInRange: { parameters: { - query?: never; + query: { + audience: "brothers" | "sisters" | "both"; + start: string; + end: string; + }; header?: never; path?: never; cookie?: never; @@ -3349,7 +6104,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["WeeklyContent"][]; + "application/json": components["schemas"]["BoardEvent"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3363,13 +6118,13 @@ export interface operations { }; }; }; - getWeeklyContentByYear: { + getBoardPayload: { parameters: { - query?: never; - header?: never; - path: { - year: number; + query: { + deviceId: string; }; + header?: never; + path?: never; cookie?: never; }; requestBody?: never; @@ -3380,7 +6135,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["WeeklyContent"][]; + "application/json": components["schemas"]["MusallahBoardPayload"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3394,14 +6149,11 @@ export interface operations { }; }; }; - getAdminWeeklyContent: { + getCurrentWeeklyContent: { parameters: { query?: never; header?: never; - path: { - year: number; - weekNumber: number; - }; + path?: never; cookie?: never; }; requestBody?: never; @@ -3426,7 +6178,7 @@ export interface operations { }; }; }; - saveWeeklyContent: { + getPublicWeeklyContent: { parameters: { query?: never; header?: never; @@ -3436,11 +6188,7 @@ export interface operations { }; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["WeeklyContentRequest"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -3462,13 +6210,12 @@ export interface operations { }; }; }; - deleteWeeklyContent: { + getJob: { parameters: { query?: never; header?: never; path: { - year: number; - weekNumber: number; + jobId: string; }; cookie?: never; }; @@ -3480,7 +6227,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["EmailJobStatus"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3494,12 +6241,10 @@ export interface operations { }; }; }; - createMediaEvent: { + getAllEvents: { parameters: { query: { - eventName: string; - eventDate: string; - status?: "UPCOMING" | "ONGOING" | "PAST"; + pageable: components["schemas"]["Pageable"]; }; header?: never; path?: never; @@ -3513,7 +6258,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["PageEventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3527,14 +6272,18 @@ export interface operations { }; }; }; - getAllEventsForAdmin: { + createEvent: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["CreateEventRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -3542,7 +6291,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MediaEvent"][]; + "application/json": components["schemas"]["EventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3556,16 +6305,18 @@ export interface operations { }; }; }; - featureUpload: { + createFullEvent: { parameters: { query?: never; header?: never; - path: { - uploadId: string; - }; + path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["CreateFullEventRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -3573,7 +6324,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["FullEventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3587,11 +6338,13 @@ export interface operations { }; }; }; - getAvailablePermissions: { + getEventById: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -3602,7 +6355,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PermissionResponse"][]; + "application/json": components["schemas"]["EventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3616,14 +6369,20 @@ export interface operations { }; }; }; - getAvailableRoles: { + updateEvent: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateEventRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -3631,7 +6390,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["RoleDefinitionResponse"][]; + "application/json": components["schemas"]["EventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3645,12 +6404,12 @@ export interface operations { }; }; }; - approveUpload: { + deleteEvent: { parameters: { query?: never; header?: never; path: { - uploadId: string; + id: string; }; cookie?: never; }; @@ -3661,9 +6420,7 @@ export interface operations { headers: { [name: string]: unknown; }; - content: { - "application/json": components["schemas"]["MessageResponse"]; - }; + content?: never; }; /** @description Request failed; body carries a human-readable message */ default: { @@ -3676,16 +6433,24 @@ export interface operations { }; }; }; - deleteUpload: { + importAttendees: { parameters: { query?: never; header?: never; path: { - uploadId: string; + id: string; }; cookie?: never; }; - requestBody?: never; + requestBody?: { + content: { + "multipart/form-data": { + config: components["schemas"]["ImportConfig"]; + /** Format: binary */ + file: string; + }; + }; + }; responses: { /** @description OK */ 200: { @@ -3693,7 +6458,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ImportResult"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3707,12 +6472,12 @@ export interface operations { }; }; }; - unapproveUpload: { + getTicketTypesByEvent: { parameters: { query?: never; header?: never; path: { - uploadId: string; + id: string; }; cookie?: never; }; @@ -3724,7 +6489,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketTypeResponse"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3738,16 +6503,20 @@ export interface operations { }; }; }; - unfeatureUpload: { + createTicketType: { parameters: { query?: never; header?: never; path: { - uploadId: string; + id: string; }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["CreateTicketTypeRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -3755,7 +6524,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketTypeResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3769,18 +6538,15 @@ export interface operations { }; }; }; - getAdminUploads: { + getTicketsByEvent: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; + query: { + pageable: components["schemas"]["Pageable"]; }; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -3791,7 +6557,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAdminUploadDto"]; + "application/json": components["schemas"]["PageTicketResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3805,18 +6571,13 @@ export interface operations { }; }; }; - getApprovedUploads: { + resendAllTickets: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -3827,7 +6588,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAdminUploadDto"]; + "application/json": components["schemas"]["EmailJobAccepted"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3841,18 +6602,13 @@ export interface operations { }; }; }; - getFeaturedUploads: { + sendMissingTickets: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; + query?: never; + header?: never; + path: { + id: string; }; - header?: never; - path?: never; cookie?: never; }; requestBody?: never; @@ -3863,7 +6619,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAdminUploadDto"]; + "application/json": components["schemas"]["EmailJobAccepted"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3877,18 +6633,13 @@ export interface operations { }; }; }; - getPendingUploads: { + getZonesByEvent: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -3899,7 +6650,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageAdminUploadDto"]; + "application/json": components["schemas"]["ZoneResponse"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3913,16 +6664,18 @@ export interface operations { }; }; }; - createUser: { + addZoneToEvent: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["CreateUserRequest"]; + "application/json": components["schemas"]["AddZoneRequest"]; }; }; responses: { @@ -3932,7 +6685,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ZoneResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3946,18 +6699,17 @@ export interface operations { }; }; }; - verifyUser: { + getOrders: { parameters: { - query?: never; + query?: { + eventId?: string; + externalRef?: string; + }; header?: never; path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["VerifyUserRequest"]; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -3965,7 +6717,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["OrderResponse"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -3979,18 +6731,16 @@ export interface operations { }; }; }; - addRoleToUser: { + createOrder_1: { parameters: { query?: never; header?: never; - path: { - userId: string; - }; + path?: never; cookie?: never; }; requestBody: { content: { - "application/json": "USER" | "ADMIN" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "ROOT"; + "application/json": components["schemas"]["CreateOrderRequest"]; }; }; responses: { @@ -4000,7 +6750,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["OrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4014,20 +6764,16 @@ export interface operations { }; }; }; - grantUserPermission: { + getOrder_1: { parameters: { query?: never; header?: never; path: { - userId: string; + id: string; }; cookie?: never; }; - requestBody: { - content: { - "application/json": "MEDIA_UPLOAD_SELF" | "MEDIA_UPLOAD_MODERATE" | "MEDIA_UPLOAD_READ" | "MEDIA_EVENT_WRITE" | "BOARD_CONTENT_READ" | "BOARD_POSTER_WRITE" | "BOARD_EVENT_WRITE" | "BOARD_SOCIAL_WRITE" | "BOARD_WEEKLY_WRITE" | "BOARD_TICKER_WRITE" | "BOARD_CONFIG_READ" | "BOARD_CONFIG_WRITE" | "BOARD_REFRESH" | "BOARD_DEVICE_READ" | "BOARD_DEVICE_ENROLL" | "BOARD_DEVICE_REVOKE" | "BOARD_TELEMETRY_SUBSCRIBE" | "BOARD_COMMAND_BENIGN" | "BOARD_COMMAND_DISRUPTIVE" | "BOARD_COMMAND_INSPECT" | "IAM_USER_READ" | "IAM_USER_WRITE" | "IAM_ROLE_GRANT" | "AUDIT_READ"; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -4035,7 +6781,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["OrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4049,20 +6795,16 @@ export interface operations { }; }; }; - removeRoleFromUser: { + cancelOrder_1: { parameters: { query?: never; header?: never; path: { - userId: string; + id: string; }; cookie?: never; }; - requestBody: { - content: { - "application/json": "USER" | "ADMIN" | "BOARD_VIEWER" | "BOARD_EDITOR" | "BOARD_ADMIN" | "ROOT"; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -4070,7 +6812,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["OrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4084,20 +6826,16 @@ export interface operations { }; }; }; - revokeUserPermission: { + confirmManualPayment: { parameters: { query?: never; header?: never; path: { - userId: string; + id: string; }; cookie?: never; }; - requestBody: { - content: { - "application/json": "MEDIA_UPLOAD_SELF" | "MEDIA_UPLOAD_MODERATE" | "MEDIA_UPLOAD_READ" | "MEDIA_EVENT_WRITE" | "BOARD_CONTENT_READ" | "BOARD_POSTER_WRITE" | "BOARD_EVENT_WRITE" | "BOARD_SOCIAL_WRITE" | "BOARD_WEEKLY_WRITE" | "BOARD_TICKER_WRITE" | "BOARD_CONFIG_READ" | "BOARD_CONFIG_WRITE" | "BOARD_REFRESH" | "BOARD_DEVICE_READ" | "BOARD_DEVICE_ENROLL" | "BOARD_DEVICE_REVOKE" | "BOARD_TELEMETRY_SUBSCRIBE" | "BOARD_COMMAND_BENIGN" | "BOARD_COMMAND_DISRUPTIVE" | "BOARD_COMMAND_INSPECT" | "IAM_USER_READ" | "IAM_USER_WRITE" | "IAM_ROLE_GRANT" | "AUDIT_READ"; - }; - }; + requestBody?: never; responses: { /** @description OK */ 200: { @@ -4105,7 +6843,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["OrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4119,18 +6857,13 @@ export interface operations { }; }; }; - getAllUsers: { + completeMockPayment: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; - path?: never; + path: { + orderId: string; + }; cookie?: never; }; requestBody?: never; @@ -4141,7 +6874,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageUserInfoResponse"]; + "application/json": components["schemas"]["OrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4155,44 +6888,28 @@ export interface operations { }; }; }; - enrollAgent: { + webhook: { parameters: { query?: never; header?: never; - path?: never; + path: { + providerId: string; + }; cookie?: never; }; - requestBody: { + requestBody?: { content: { - "application/json": components["schemas"]["AgentEnrollRequest"]; + "application/json": string; }; }; responses: { - /** @description Device enrolled; identity and websocket URL returned */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["AgentEnrollResponse"]; - }; - }; - /** @description Public key malformed or unsupported */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Enrollment token invalid, expired, or already used */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": string; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4206,30 +6923,29 @@ export interface operations { }; }; }; - changePassword: { + getTicketZoneEntryCount: { parameters: { query?: never; header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ChangePasswordRequest"]; + path: { + ticketId: string; + zoneId: string; }; + cookie?: never; }; + requestBody?: never; responses: { - /** @description Password changed; all sessions revoked */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": number; }; }; - /** @description Current password incorrect or new password rejected */ - 400: { + /** @description Request failed; body carries a human-readable message */ + default: { headers: { [name: string]: unknown; }; @@ -4237,13 +6953,28 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; - /** @description Not signed in */ - 403: { + }; + }; + getScanHistoryForEvent: { + parameters: { + query: { + pageable: components["schemas"]["Pageable"]; + }; + header?: never; + path: { + eventId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["PageScanEventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4257,33 +6988,26 @@ export interface operations { }; }; }; - requestPasswordReset: { + scanTicket: { parameters: { - query: { - email: string; - }; + query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["ScanRequest"]; + }; + }; responses: { - /** @description Request accepted */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Email missing */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ScanResult"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4297,26 +7021,26 @@ export interface operations { }; }; }; - logout: { + scanByQr: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; - requestBody?: { + requestBody: { content: { - "application/json": components["schemas"]["TokenRefreshRequest"]; + "application/json": components["schemas"]["QrScanRequest"]; }; }; responses: { - /** @description Session ended */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ScanResult"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4330,31 +7054,24 @@ export interface operations { }; }; }; - logoutAllDevices: { + getScanHistoryForTicket: { parameters: { query?: never; header?: never; - path?: never; + path: { + ticketId: string; + }; cookie?: never; }; requestBody?: never; responses: { - /** @description All sessions ended */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Not signed in */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ScanEventResponse"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4368,7 +7085,7 @@ export interface operations { }; }; }; - refreshToken: { + validateTicketForZone: { parameters: { query?: never; header?: never; @@ -4377,26 +7094,17 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["TokenRefreshRequest"]; + "application/json": components["schemas"]["ValidateRequest"]; }; }; responses: { - /** @description New access and refresh tokens issued */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["TokenRefreshResponse"]; - }; - }; - /** @description Refresh token unknown, expired or already revoked */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["ValidationResult"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4410,35 +7118,26 @@ export interface operations { }; }; }; - resetPassword: { + getScanHistoryForZone: { parameters: { - query?: never; + query: { + pageable: components["schemas"]["Pageable"]; + }; header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ResetPasswordRequest"]; + path: { + zoneId: string; }; + cookie?: never; }; + requestBody?: never; responses: { - /** @description Password reset */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Token or password missing, invalid or expired */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["PageScanEventResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4452,30 +7151,28 @@ export interface operations { }; }; }; - signIn: { + getAllTicketTypes: { parameters: { - query?: never; + query: { + pageable: components["schemas"]["Pageable"]; + }; header?: never; path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": components["schemas"]["LoginRequest"]; - }; - }; + requestBody?: never; responses: { - /** @description Authenticated; access and refresh tokens issued */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["JwtResponse"]; + "application/json": components["schemas"]["PageTicketTypeResponse"]; }; }; - /** @description Bad credentials or disabled account */ - 401: { + /** @description Request failed; body carries a human-readable message */ + default: { headers: { [name: string]: unknown; }; @@ -4483,13 +7180,26 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; - /** @description Too many failed attempts; account temporarily locked */ - 429: { + }; + }; + getTicketTypeById: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketTypeResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4503,30 +7213,32 @@ export interface operations { }; }; }; - signUp: { + updateTicketType: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["SignupRequest"]; + "application/json": components["schemas"]["UpdateTicketTypeRequest"]; }; }; responses: { - /** @description Account created; verification email sent */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketTypeResponse"]; }; }; - /** @description Validation failed or email already registered */ - 400: { + /** @description Request failed; body carries a human-readable message */ + default: { headers: { [name: string]: unknown; }; @@ -4534,6 +7246,26 @@ export interface operations { "application/json": components["schemas"]["MessageResponse"]; }; }; + }; + }; + deleteTicketType: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; /** @description Request failed; body carries a human-readable message */ default: { headers: { @@ -4545,7 +7277,7 @@ export interface operations { }; }; }; - validateResetToken: { + createTicket: { parameters: { query?: never; header?: never; @@ -4554,26 +7286,17 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["ValidateResetTokenRequest"]; + "application/json": components["schemas"]["CreateTicketRequest"]; }; }; responses: { - /** @description Token is valid */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Token missing, invalid or expired */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4587,31 +7310,24 @@ export interface operations { }; }; }; - validateToken: { + getTicketById: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; responses: { - /** @description Token valid; user returned */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["TokenValidationResponse"]; - }; - }; - /** @description Token missing, expired, or user no longer exists */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4625,35 +7341,28 @@ export interface operations { }; }; }; - verifyEmail: { + updateTicket: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody: { content: { - "application/json": components["schemas"]["VerifyEmailRequest"]; + "application/json": components["schemas"]["UpdateTicketRequest"]; }; }; responses: { - /** @description Email verified */ + /** @description OK */ 200: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MessageResponse"]; - }; - }; - /** @description Missing, invalid or expired token */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["MessageResponse"]; + "application/json": components["schemas"]["TicketResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4667,11 +7376,13 @@ export interface operations { }; }; }; - getPublicEvents: { + deleteTicket: { parameters: { query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -4681,9 +7392,7 @@ export interface operations { headers: { [name: string]: unknown; }; - content: { - "application/json": components["schemas"]["MediaEvent"][]; - }; + content?: never; }; /** @description Request failed; body carries a human-readable message */ default: { @@ -4696,7 +7405,7 @@ export interface operations { }; }; }; - getPublicEventById: { + resendTicket: { parameters: { query?: never; header?: never; @@ -4713,7 +7422,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MediaEvent"]; + "application/json": components["schemas"]["EmailJobAccepted"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4727,15 +7436,10 @@ export interface operations { }; }; }; - getGalleryUploads: { + getAllZones: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; + query: { + pageable: components["schemas"]["Pageable"]; }; header?: never; path?: never; @@ -4749,7 +7453,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageGalleryItemDto"]; + "application/json": components["schemas"]["PageZoneResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4763,19 +7467,12 @@ export interface operations { }; }; }; - getGalleryByEvent: { + getZoneById: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; path: { - eventId: string; + id: string; }; cookie?: never; }; @@ -4787,7 +7484,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageGalleryItemDto"]; + "application/json": components["schemas"]["ZoneResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4801,16 +7498,20 @@ export interface operations { }; }; }; - getPublicBoardConfig: { + updateZone: { parameters: { - query: { - deviceId: string; - }; + query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateZoneRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -4818,7 +7519,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["DeviceConfig"]; + "application/json": components["schemas"]["ZoneResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4832,13 +7533,13 @@ export interface operations { }; }; }; - getUpcomingEvents: { + deleteZone: { parameters: { - query: { - audience: "brothers" | "sisters" | "both"; - }; + query?: never; header?: never; - path?: never; + path: { + id: string; + }; cookie?: never; }; requestBody?: never; @@ -4848,9 +7549,7 @@ export interface operations { headers: { [name: string]: unknown; }; - content: { - "application/json": components["schemas"]["BoardEvent"][]; - }; + content?: never; }; /** @description Request failed; body carries a human-readable message */ default: { @@ -4863,15 +7562,20 @@ export interface operations { }; }; }; - getEventsInRange: { + getUploadsByEvent: { parameters: { - query: { - audience: "brothers" | "sisters" | "both"; - start: string; - end: string; + query?: { + /** @description Zero-based page index (0..N) */ + page?: number; + /** @description The size of the page to be returned */ + size?: number; + /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ + sort?: string[]; }; header?: never; - path?: never; + path: { + eventId: string; + }; cookie?: never; }; requestBody?: never; @@ -4882,7 +7586,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["BoardEvent"][]; + "application/json": components["schemas"]["PageUploadDto"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4896,11 +7600,9 @@ export interface operations { }; }; }; - getBoardPayload: { + getUploadLimits: { parameters: { - query: { - deviceId: string; - }; + query?: never; header?: never; path?: never; cookie?: never; @@ -4913,7 +7615,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["MusallahBoardPayload"]; + "application/json": components["schemas"]["UploadLimitsResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4927,11 +7629,22 @@ export interface operations { }; }; }; - getCurrentWeeklyContent: { + completeDirectUpload: { parameters: { - query?: never; + query: { + objectKey: string; + filename: string; + contentType: string; + fileSize: number; + instagramHandle?: string; + description?: string; + anon?: boolean; + expectedSha256: string; + }; header?: never; - path?: never; + path: { + eventId: string; + }; cookie?: never; }; requestBody?: never; @@ -4942,7 +7655,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["WeeklyContent"]; + "application/json": components["schemas"]["UploadCompletionResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4956,13 +7669,17 @@ export interface operations { }; }; }; - getPublicWeeklyContent: { + generatePresignedUploadUrl: { parameters: { - query?: never; + query: { + filename: string; + contentType: string; + fileSize: number; + expectedSha256: string; + }; header?: never; path: { - year: number; - weekNumber: number; + eventId: string; }; cookie?: never; }; @@ -4974,7 +7691,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["WeeklyContent"]; + "application/json": components["schemas"]["PresignedUploadResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -4988,19 +7705,12 @@ export interface operations { }; }; }; - getUploadsByEvent: { + getUploadById: { parameters: { - query?: { - /** @description Zero-based page index (0..N) */ - page?: number; - /** @description The size of the page to be returned */ - size?: number; - /** @description Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */ - sort?: string[]; - }; + query?: never; header?: never; path: { - eventId: string; + uploadId: string; }; cookie?: never; }; @@ -5012,7 +7722,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageUploadDto"]; + "application/json": components["schemas"]["UploadDto"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5026,7 +7736,7 @@ export interface operations { }; }; }; - getUploadLimits: { + getUserOrders: { parameters: { query?: never; header?: never; @@ -5041,7 +7751,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["UploadLimitsResponse"]; + "application/json": components["schemas"]["MinbarOrderResponse"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5055,25 +7765,18 @@ export interface operations { }; }; }; - completeDirectUpload: { + createOrder: { parameters: { - query: { - objectKey: string; - filename: string; - contentType: string; - fileSize: number; - instagramHandle?: string; - description?: string; - anon?: boolean; - expectedSha256: string; - }; + query?: never; header?: never; - path: { - eventId: string; - }; + path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": components["schemas"]["MinbarCreateOrderRequest"]; + }; + }; responses: { /** @description OK */ 200: { @@ -5081,7 +7784,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["UploadCompletionResponse"]; + "application/json": components["schemas"]["MinbarOrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5095,17 +7798,12 @@ export interface operations { }; }; }; - generatePresignedUploadUrl: { + getOrder: { parameters: { - query: { - filename: string; - contentType: string; - fileSize: number; - expectedSha256: string; - }; + query?: never; header?: never; path: { - eventId: string; + id: string; }; cookie?: never; }; @@ -5117,7 +7815,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PresignedUploadResponse"]; + "application/json": components["schemas"]["MinbarOrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5131,12 +7829,12 @@ export interface operations { }; }; }; - getUploadById: { + cancelOrder: { parameters: { query?: never; header?: never; path: { - uploadId: string; + id: string; }; cookie?: never; }; @@ -5148,7 +7846,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["UploadDto"]; + "application/json": components["schemas"]["MinbarOrderResponse"]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5224,7 +7922,7 @@ export interface operations { }; }; }; - getUserStats: { + getUserTickets: { parameters: { query?: never; header?: never; @@ -5239,7 +7937,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["UserStatsResponse"]; + "application/json": components["schemas"]["BoardingPassView"][]; }; }; /** @description Request failed; body carries a human-readable message */ @@ -5275,7 +7973,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PageGalleryItemDto"]; + "application/json": components["schemas"]["PageUploadDto"]; }; }; /** @description Request failed; body carries a human-readable message */ diff --git a/src/components/board/PrayerSpaceEditor.jsx b/src/components/board/PrayerSpaceEditor.jsx new file mode 100644 index 0000000..4175814 --- /dev/null +++ b/src/components/board/PrayerSpaceEditor.jsx @@ -0,0 +1,712 @@ +import { useEffect, useMemo, useState } from 'react'; +import { ChevronDown, ChevronUp, Plus, Trash2 } from 'lucide-react'; +import PrayerSpaceService from '../../services/PrayerSpaceService'; +import { + AUDIENCE_OPTIONS, + PRAYER_SPACE_TYPES, + defaultAudienceForSpaceType, +} from '../../models/board'; +import { Button, Field, Input, Modal, Select, Textarea, useToast } from '../ui'; + +/** + * Create or edit one prayer space. + * + * The form is long — twenty fields and three lists — so it is grouped by the + * question each group answers: what is it, where is it, how does it look on a + * phone, and how do you walk there. That last group is the one people actually + * come here to fix. + * + * Two things about it are contract, not taste: + * + * - There is no "summary" field. The app composes "2 min walk from the CCT + * main entrance" out of `walkTimeMinutes` and `startingPoint`, so those are + * the boxes; writing the sentence by hand is not an option the API offers. + * - A save sends only what changed (`PrayerSpaceService.changes`). Fixing a + * typo in step 3 must not re-send nineteen untouched fields over whatever + * somebody else edited in the meantime. + */ + +/** Server messages, mapped to the box they belong under. Most specific first. */ +const SERVER_MESSAGE_FIELDS = [ + [/latitude and longitude/i, 'latitude'], + [/latitude/i, 'latitude'], + [/longitude/i, 'longitude'], + [/capacity/i, 'capacity'], + [/walk time/i, 'walkTimeMinutes'], + [/maps url/i, 'mapsUrl'], + [/image url/i, 'imageUrl'], + [/instruction/i, 'steps'], + [/entrance/i, 'entranceName'], + [/^name|name is required/i, 'name'], + [/tag/i, 'tag'], + [/building/i, 'building'], +]; + +function fieldForServerMessage(message) { + return SERVER_MESSAGE_FIELDS.find(([pattern]) => pattern.test(message ?? ''))?.[1] ?? null; +} + +/** + * A link the app will hand to a phone. A malformed one still renders on the + * card and fails only when somebody taps it, standing in a corridor. + * @returns {string} the problem, or '' when the value is fine or empty + */ +function urlProblem(value, label) { + const trimmed = value.trim(); + if (!trimmed) return ''; + let parsed; + try { + parsed = new URL(trimmed); + } catch { + return `Enter a complete ${label}, including https://`; + } + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + return 'Only http:// and https:// links can be opened from the app.'; + } + return ''; +} + +function numberOrNull(value) { + const trimmed = value.trim(); + if (!trimmed) return null; + const parsed = Number(trimmed); + return Number.isFinite(parsed) ? parsed : null; +} + +/** + * Everything the server would reject, checked here so it comes back as a + * message under the box rather than a toast about the whole form. + * + * `before` is null when creating. On an edit it carries the one rule that is + * not a validation at all but a shape of the API: a null number means "leave it + * alone", so a number that has been stored cannot be cleared, only changed. + */ +function problems(form, before) { + const errors = {}; + + const required = (field, message) => { + const value = form[field].trim(); + if (!value) errors[field] = message; + else if (value.length > 255) errors[field] = 'Capped at 255 characters.'; + }; + + required('name', 'Give the space a name.'); + required('tag', 'Give it a short tag — the app prints it under the name.'); + required('building', 'Say which building it is in.'); + + for (const field of ['floor', 'roomInfo', 'startingPoint', 'entranceName', 'entranceDescription']) { + if (form[field].trim().length > 255) errors[field] = 'Capped at 255 characters.'; + } + + const capacity = numberOrNull(form.capacity); + if (form.capacity.trim() && (capacity === null || !Number.isInteger(capacity) || capacity < 1)) { + errors.capacity = 'Capacity must be a whole number above zero.'; + } + + const walkTime = numberOrNull(form.walkTimeMinutes); + if (form.walkTimeMinutes.trim() && (walkTime === null || !Number.isInteger(walkTime) || walkTime < 0)) { + errors.walkTimeMinutes = 'Walk time must be a whole number of minutes, and cannot be negative.'; + } + + const hasLat = form.latitude.trim() !== ''; + const hasLon = form.longitude.trim() !== ''; + const latitude = numberOrNull(form.latitude); + const longitude = numberOrNull(form.longitude); + + if (hasLat !== hasLon) { + errors[hasLat ? 'longitude' : 'latitude'] = + 'Coordinates go in as a pair. One on its own is not half a location, it is a point in the ocean.'; + } + if (hasLat && (latitude === null || latitude < -90 || latitude > 90)) { + errors.latitude = 'Latitude must be between -90 and 90.'; + } + if (hasLon && (longitude === null || longitude < -180 || longitude > 180)) { + errors.longitude = 'Longitude must be between -180 and 180.'; + } + + const mapsProblem = urlProblem(form.mapsUrl, 'link'); + if (mapsProblem) errors.mapsUrl = mapsProblem; + const imageProblem = urlProblem(form.imageUrl, 'image URL'); + if (imageProblem) errors.imageUrl = imageProblem; + + if (form.steps.some((step) => !step.instruction.trim())) { + errors.steps = 'Every step needs an instruction. Fill the empty one in, or remove it.'; + } + + // Emptying both coordinate boxes is a real edit — it travels as + // `clearCoordinates` and un-pins the space — so it is not checked here. The + // other two numbers have no such flag: null means "leave alone", and a + // stored capacity or walk time can be corrected but not removed. + if (before) { + for (const field of ['capacity', 'walkTimeMinutes']) { + if (before[field] != null && !form[field].trim()) { + errors[field] = + 'This cannot be emptied — the API reads a missing number as "leave it alone". Put a number back, or leave the old one.'; + } + } + } + + return errors; +} + +/** A titled run of fields, ruled off from the one above it. */ +function FormSection({ title, note, children }) { + return ( +
+

{title}

+ {note &&

{note}

} +
{children}
+
+ ); +} + +/** + * An unordered list of short strings — amenities, tips. + * + * A row per value rather than a comma-separated box: these end up as chips on a + * card, and a comma inside one ("wudu area, ground floor") would silently split + * into two. + */ +function StringList({ label, hint, error, values, onChange, placeholder, addLabel }) { + const set = (index, value) => onChange(values.map((v, i) => (i === index ? value : v))); + const remove = (index) => onChange(values.filter((_, i) => i !== index)); + + return ( + +
+ {values.map((value, index) => ( +
+ set(index, e.target.value)} + /> +
+ ))} + +
+
+ ); +} + +export default function PrayerSpaceEditor({ open, space, onSaved, onClose }) { + const toast = useToast(); + const creating = !space; + + const [form, setForm] = useState(() => PrayerSpaceService.emptyForm()); + const [saving, setSaving] = useState(false); + const [formError, setFormError] = useState(null); + const [fieldErrors, setFieldErrors] = useState({}); + // Once somebody picks an audience by hand, the room type stops overriding it. + const [audiencePicked, setAudiencePicked] = useState(false); + + // Reseed whenever the dialog opens, so a cancelled edit cannot leak into the + // next one and so the baseline a save diffs against is the record just loaded. + useEffect(() => { + if (!open) return; + setForm(space ? PrayerSpaceService.toForm(space) : PrayerSpaceService.emptyForm()); + setFormError(null); + setFieldErrors({}); + setAudiencePicked(false); + }, [open, space]); + + const patch = (changes) => setForm((f) => ({ ...f, ...changes })); + + /** + * On create the audience follows the room type until it is set by hand — + * which is exactly what the server does with an omitted `audience`, made + * visible. On edit it never moves: the server does not re-derive there, and + * silently re-listing a sisters-only reflection bay to the whole campus + * because somebody corrected its type is not a fix, it is an incident. + */ + const changeType = (type) => { + if (creating && !audiencePicked) { + patch({ type, audience: defaultAudienceForSpaceType(type) }); + } else { + patch({ type }); + } + }; + + const changeAudience = (audience) => { + setAudiencePicked(true); + patch({ audience }); + }; + + const audienceHint = creating + ? audiencePicked + ? 'Set by hand — it no longer follows the room type.' + : `Following the room type. Leave it and the space is listed to ${ + form.audience === 'both' ? 'everyone' : form.audience + }.` + : 'Who the space is listed to. Changing the room type does not move it — the server never re-derives audience on an edit.'; + + // On an edit, the diff is also the answer to "is there anything to save". + const pending = useMemo( + () => (space ? PrayerSpaceService.changes(space, form) : null), + [space, form] + ); + const dirty = creating || Object.keys(pending ?? {}).length > 0; + + const setStep = (index, changes) => + setForm((f) => ({ + ...f, + steps: f.steps.map((s, i) => (i === index ? { ...s, ...changes } : s)), + })); + + const moveStep = (index, delta) => + setForm((f) => { + const next = [...f.steps]; + const target = index + delta; + if (target < 0 || target >= next.length) return f; + [next[index], next[target]] = [next[target], next[index]]; + return { ...f, steps: next }; + }); + + const save = async () => { + const found = problems(form, space); + if (Object.keys(found).length > 0) { + setFieldErrors(found); + setFormError('Some of this will not save as it stands — see the fields below.'); + return; + } + + setSaving(true); + setFormError(null); + setFieldErrors({}); + try { + if (creating) { + const created = await PrayerSpaceService.create(form); + toast.success(`${created.name} created.`); + onSaved(created, true); + } else { + if (Object.keys(pending).length === 0) { + onClose(); + return; + } + const updated = await PrayerSpaceService.update(space.id, pending); + toast.success(`${updated.name} updated.`); + onSaved(updated, false); + } + } catch (err) { + // The API answers with a MessageResponse carrying a sentence a person + // wrote. Show it whole, and put it under the box it names when it names one. + setFormError(err.message); + const field = fieldForServerMessage(err.message); + if (field) setFieldErrors({ [field]: err.message }); + } finally { + setSaving(false); + } + }; + + return ( + + + + + } + > +
+ {formError && ( +

+ {formError} +

+ )} + + +
+ + patch({ name: e.target.value })} + /> + + + patch({ tag: e.target.value })} + /> + +
+ +
+ + + + + + +
+
+ + +
+ + patch({ building: e.target.value })} + /> + + + patch({ floor: e.target.value })} + /> + + + patch({ roomInfo: e.target.value })} + /> + +
+ +
+ + patch({ capacity: e.target.value })} + /> + + + patch({ latitude: e.target.value })} + /> + + + patch({ longitude: e.target.value })} + /> + +
+ + + patch({ mapsUrl: e.target.value })} + /> + +
+ + + + patch({ imageUrl: e.target.value })} + /> + + + +