diff --git a/components/formElements/TextEditor.tsx b/components/formElements/TextEditor.tsx index d6e187f..5f024cc 100644 --- a/components/formElements/TextEditor.tsx +++ b/components/formElements/TextEditor.tsx @@ -10,6 +10,7 @@ import { TextStyle } from '@tiptap/extension-text-style' import Highlight from '@tiptap/extension-highlight' import Image from '@tiptap/extension-image' import React, { useCallback, useEffect, useRef, useState } from 'react' +import { buttonVariants } from '@heroui/react' import { Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from '@/components/heroui/Dropdown' import { addToast } from '@/lib/toast' @@ -20,7 +21,6 @@ import FileUpload from '@/components/media/FileUpload' import ConsumerModal from '@/components/consumer/ConsumerModal' import { fileAddress, coerceToString } from '@/helpers' import { cn } from '@/lib/cn' -import { buttonVariants } from '@heroui/react' const CONSUMER_TOOLBAR_TRIGGER_CLASS = 'min-h-10 rounded-[10px] border border-consumer-border bg-consumer-surface px-3 text-sm font-medium text-consumer-text outline-none' diff --git a/components/heroui/Dropdown.tsx b/components/heroui/Dropdown.tsx index 9f786fe..acb3ead 100644 --- a/components/heroui/Dropdown.tsx +++ b/components/heroui/Dropdown.tsx @@ -137,8 +137,7 @@ function isButtonLikeTrigger(child: ReactElement): boolean { const displayName = typeof child.type === 'function' || typeof child.type === 'object' - ? (child.type as { displayName?: string; name?: string }).displayName || - (child.type as { name?: string }).name + ? (child.type as { displayName?: string; name?: string }).displayName || (child.type as { name?: string }).name : undefined return displayName === 'ConsumerButton' || displayName === 'Button' diff --git a/openapi.json b/openapi.json index 0e5f850..9b6dd1f 100644 --- a/openapi.json +++ b/openapi.json @@ -2847,6 +2847,74 @@ ] } }, + "/api/v1/notifications/me/read-all": { + "patch": { + "description": "Marks every currently unread in-app notification owned by the authenticated user, including notifications outside loaded pages.", + "operationId": "NotificationsController_markAllAsRead", + "parameters": [], + "responses": { + "200": { + "description": "Number of notifications updated and the shared read timestamp.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationMarkAllReadResponseDto" + } + } + } + }, + "400": { + "description": "Validation error - request body, parameters, or query string are invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseDto" + } + } + } + }, + "401": { + "description": "Unauthorized - invalid or missing JWT token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseDto" + } + } + } + }, + "403": { + "description": "Forbidden - Insufficient permissions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseDto" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseDto" + } + } + } + } + }, + "security": [ + { + "JWT-auth": [] + } + ], + "summary": "Mark all my in-app notifications as read", + "tags": [ + "Notifications" + ] + } + }, "/api/v1/notifications/{id}/read": { "patch": { "description": "Sets read_at for the current user on the given in-app notification.", @@ -7783,6 +7851,112 @@ ] } }, + "/api/v1/events/{eventId}/faqs/page": { + "get": { + "operationId": "EventFaqsController_listPage", + "parameters": [ + { + "name": "eventId", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "1-based page", + "schema": { + "minimum": 1, + "default": 1, + "type": "number" + } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Page size (alias accepted: limit)", + "schema": { + "minimum": 1, + "maximum": 100, + "default": 20, + "type": "number" + } + }, + { + "name": "sort", + "required": false, + "in": "query", + "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", + "schema": { + "example": "-createdAt", + "type": "string" + } + }, + { + "name": "filters", + "required": false, + "in": "query", + "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + { + "name": "resultType", + "required": false, + "in": "query", + "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", + "schema": { + "type": "number", + "enum": [ + 0, + 1 + ] + } + } + ], + "responses": { + "200": { + "description": "Event FAQs ordered by sortOrder and id.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventFaqResponseDto" + }, + "description": "Array of items for the current page." + }, + "response": { + "$ref": "#/components/schemas/PaginationMetaDto", + "description": "Pagination metadata for the current query." + } + }, + "required": [ + "items", + "response" + ] + } + } + } + } + }, + "summary": "Paginated FAQs for an event", + "tags": [ + "Event Extras" + ] + } + }, "/api/v1/events/{eventId}/faqs/{id}": { "patch": { "operationId": "EventFaqsController_update", @@ -8333,9 +8507,11 @@ "tags": [ "Event Extras" ] - }, + } + }, + "/api/v1/events/{eventId}/guest-list-links/invited-guests/page": { "get": { - "operationId": "EventGuestListLinksController_getInvitedGuests", + "operationId": "EventGuestListLinksController_getInvitedGuestsPage", "parameters": [ { "name": "eventId", @@ -8344,18 +8520,90 @@ "schema": { "type": "string" } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "1-based page", + "schema": { + "minimum": 1, + "default": 1, + "type": "number" + } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Page size (alias accepted: limit)", + "schema": { + "minimum": 1, + "maximum": 100, + "default": 20, + "type": "number" + } + }, + { + "name": "sort", + "required": false, + "in": "query", + "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", + "schema": { + "example": "-createdAt", + "type": "string" + } + }, + { + "name": "filters", + "required": false, + "in": "query", + "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + { + "name": "resultType", + "required": false, + "in": "query", + "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", + "schema": { + "type": "number", + "enum": [ + 0, + 1 + ] + } } ], "responses": { "200": { - "description": "", + "description": "Paginated list response", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/InvitedGuestResponseDto" - } + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InvitedGuestResponseDto" + }, + "description": "Array of items for the current page." + }, + "response": { + "$ref": "#/components/schemas/PaginationMetaDto", + "description": "Pagination metadata for the current query." + } + }, + "required": [ + "items", + "response" + ] } } } @@ -8366,7 +8614,7 @@ "bearer": [] } ], - "summary": "Guests selected to invite to this event, for its organizer", + "summary": "Paginated guests selected to invite to this event", "tags": [ "Event Extras" ] @@ -8409,31 +8657,101 @@ ] } }, - "/api/v1/events/{eventId}/guest-list-links": { + "/api/v1/events/{eventId}/guest-list-links/page": { "get": { - "description": "Unpaginated, same documented exception as event_faqs/event_media — an organizer linking dozens of guest lists to one event is not a realistic scenario.", - "operationId": "EventGuestListLinksController_list", + "operationId": "EventGuestListLinksController_listPage", "parameters": [ { "name": "eventId", "required": true, "in": "path", "schema": { - "format": "uuid", "type": "string" } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "1-based page", + "schema": { + "minimum": 1, + "default": 1, + "type": "number" + } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Page size (alias accepted: limit)", + "schema": { + "minimum": 1, + "maximum": 100, + "default": 20, + "type": "number" + } + }, + { + "name": "sort", + "required": false, + "in": "query", + "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", + "schema": { + "example": "-createdAt", + "type": "string" + } + }, + { + "name": "filters", + "required": false, + "in": "query", + "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + { + "name": "resultType", + "required": false, + "in": "query", + "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", + "schema": { + "type": "number", + "enum": [ + 0, + 1 + ] + } } ], "responses": { "200": { - "description": "", + "description": "Paginated list response", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GuestListLinkResponseDto" - } + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GuestListLinkResponseDto" + }, + "description": "Array of items for the current page." + }, + "response": { + "$ref": "#/components/schemas/PaginationMetaDto", + "description": "Pagination metadata for the current query." + } + }, + "required": [ + "items", + "response" + ] } } } @@ -8444,11 +8762,13 @@ "bearer": [] } ], - "summary": "Guest lists linked to this event (unpaginated)", + "summary": "Paginated guest lists linked to this event", "tags": [ "Event Extras" ] - }, + } + }, + "/api/v1/events/{eventId}/guest-list-links": { "post": { "operationId": "EventGuestListLinksController_link", "parameters": [ @@ -16426,7 +16746,7 @@ "items": { "type": "array", "items": { - "$ref": "#/components/schemas/SupportTicketResponseDto" + "$ref": "#/components/schemas/SupportTicketSummaryResponseDto" }, "description": "Array of items for the current page." }, @@ -16455,6 +16775,82 @@ ] } }, + "/api/v1/support-tickets/{id}/messages": { + "get": { + "description": "Returns the latest messages first by cursor selection, while items inside each response are ordered oldest-to-newest. Pass `olderCursor` as `before` to load older history.", + "operationId": "SupportTicketsController_listMessagesMine", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Number of messages to return.", + "schema": { + "minimum": 1, + "maximum": 50, + "default": 20, + "type": "number" + } + }, + { + "name": "before", + "required": false, + "in": "query", + "description": "Load messages strictly older than this message id. The cursor must belong to this ticket.", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Cursor-paginated support ticket messages.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SupportTicketMessageResponseDto" + }, + "description": "Array of items for the current page." + }, + "response": { + "$ref": "#/components/schemas/PaginationMetaDto", + "description": "Pagination metadata for the current query." + } + }, + "required": [ + "items", + "response" + ] + } + } + } + } + }, + "security": [ + { + "JWT-auth": [] + } + ], + "summary": "List my support ticket messages", + "tags": [ + "Support Tickets" + ] + } + }, "/api/v1/support-tickets/{id}": { "get": { "operationId": "SupportTicketsController_getMine", @@ -16474,7 +16870,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SupportTicketResponseDto" + "$ref": "#/components/schemas/SupportTicketSummaryResponseDto" } } } @@ -16485,7 +16881,7 @@ "JWT-auth": [] } ], - "summary": "Get my support ticket thread", + "summary": "Get my support ticket metadata", "tags": [ "Support Tickets" ] @@ -16520,7 +16916,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SupportTicketResponseDto" + "$ref": "#/components/schemas/SupportTicketSummaryResponseDto" } } } @@ -16611,7 +17007,7 @@ "items": { "type": "array", "items": { - "$ref": "#/components/schemas/SupportTicketResponseDto" + "$ref": "#/components/schemas/SupportTicketSummaryResponseDto" }, "description": "Array of items for the current page." }, @@ -18860,10 +19256,68 @@ ] } }, - "/api/v1/blog-articles": { + "/api/v1/blog-articles/page": { "get": { - "operationId": "BlogArticlesController_list", + "operationId": "BlogArticlesController_listPage", "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "1-based page", + "schema": { + "minimum": 1, + "default": 1, + "type": "number" + } + }, + { + "name": "pageSize", + "required": false, + "in": "query", + "description": "Page size (alias accepted: limit)", + "schema": { + "minimum": 1, + "maximum": 100, + "default": 20, + "type": "number" + } + }, + { + "name": "sort", + "required": false, + "in": "query", + "description": "Sort field. Prefix with \"-\" for descending (e.g. \"-createdAt\").", + "schema": { + "example": "-createdAt", + "type": "string" + } + }, + { + "name": "filters", + "required": false, + "in": "query", + "description": "Column filters echoed back in the response. Values are strings; date ranges use \"from,to\".", + "schema": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + { + "name": "resultType", + "required": false, + "in": "query", + "description": "0 = JSON list (default). 1 = Excel export (returns response.fileData).", + "schema": { + "type": "number", + "enum": [ + 0, + 1 + ] + } + }, { "name": "categorySlug", "required": false, @@ -18893,26 +19347,44 @@ "required": false, "in": "query", "schema": { - "type": "boolean" + "type": "string", + "enum": [ + "true", + "false" + ] } } ], "responses": { "200": { - "description": "", + "description": "Published blog articles with deterministic pagination.", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BlogArticleSummaryResponseDto" - } + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BlogArticleSummaryResponseDto" + }, + "description": "Array of items for the current page." + }, + "response": { + "$ref": "#/components/schemas/PaginationMetaDto", + "description": "Pagination metadata for the current query." + } + }, + "required": [ + "items", + "response" + ] } } } } }, - "summary": "Published blog articles (unpaginated, for hub/sitemap pages)", + "summary": "Paginated published blog articles", "tags": [ "Blog Articles" ] @@ -19494,6 +19966,8 @@ "CAPACITY_BELOW_BOOKED", "IDENTITY_REQUIRED", "HOST_EVENT_LIMIT_REACHED", + "EVENT_FAQ_LIMIT_REACHED", + "BANK_ACCOUNT_LIMIT_REACHED", "NOT_CHECKED_IN", "BOOKING_NOT_CONFIRMED", "REVIEW_LOCKED", @@ -21405,6 +21879,23 @@ "totalUnread" ] }, + "NotificationMarkAllReadResponseDto": { + "type": "object", + "properties": { + "updatedCount": { + "type": "number", + "example": 12 + }, + "readAt": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "updatedCount", + "readAt" + ] + }, "PushVapidPublicKeyResponseDto": { "type": "object", "properties": { @@ -23035,6 +23526,7 @@ } }, "faqs": { + "maxItems": 20, "type": "array", "items": { "$ref": "#/components/schemas/CreateEventFaqDto" @@ -24581,6 +25073,9 @@ "$ref": "#/components/schemas/EventFaqResponseDto" } }, + "faqsTotal": { + "type": "number" + }, "organizer": { "$ref": "#/components/schemas/EventLandingOrganizerDto" }, @@ -24600,6 +25095,7 @@ "city", "media", "faqs", + "faqsTotal", "organizer", "reviews", "reviewsTotal" @@ -29841,6 +30337,88 @@ "messages" ] }, + "SupportTicketSummaryResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "userId": { + "type": "string", + "format": "uuid" + }, + "subject": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "general", + "account", + "event", + "booking", + "payment", + "technical" + ] + }, + "priority": { + "type": "string", + "enum": [ + "low", + "normal", + "high", + "urgent" + ] + }, + "status": { + "type": "string", + "enum": [ + "open", + "waiting_admin", + "waiting_user", + "closed" + ] + }, + "lastMessageAt": { + "type": "string", + "format": "date-time" + }, + "closedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "#/components/schemas/SupportTicketUserResponseDto" + }, + "latestMessage": { + "nullable": true, + "description": "Latest message preview only; complete history is not included.", + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/SupportTicketMessageResponseDto" + } + ] + } + }, + "required": [ + "id", + "userId", + "subject", + "category", + "priority", + "status", + "lastMessageAt", + "createdAt", + "user" + ] + }, "ReplySupportTicketDto": { "type": "object", "properties": {