Skip to content
Open
73 changes: 56 additions & 17 deletions backend/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,12 @@ definitions:
items:
type: integer
type: array
hotel_id:
type: string
limit:
maximum: 100
minimum: 1
type: integer
search:
type: string
required:
- hotel_id
type: object
GuestPage:
properties:
Expand All @@ -162,18 +158,25 @@ definitions:
GuestWithBooking:
properties:
first_name:
example: Jane
type: string
floor:
example: 3
type: integer
group_size:
example: 2
type: integer
id:
example: 530e8400-e458-41d4-a716-446655440000
type: string
last_name:
example: Doe
type: string
preferred_name:
example: Jane
type: string
room_number:
example: 301
type: integer
required:
- first_name
Expand Down Expand Up @@ -532,41 +535,36 @@ paths:
post:
consumes:
- application/json
description: Retrieves guests optionally filtered by floor
description: Creates a guest with the given data
parameters:
- description: Hotel ID (UUID)
in: header
name: X-Hotel-ID
required: true
type: string
- description: Guest filters
- description: Guest data
in: body
name: body
name: request
required: true
schema:
$ref: '#/definitions/GuestFilters'
$ref: '#/definitions/CreateGuest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/GuestPage'
$ref: '#/definitions/Guest'
"400":
description: Bad Request
description: Invalid guest body format
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
description: Internal server error
schema:
additionalProperties:
type: string
type: object
security:
- BearerAuth: []
summary: Get Guests
summary: Creates a guest
tags:
- guests
/api/v1/guests/{id}:
Expand Down Expand Up @@ -654,6 +652,47 @@ paths:
summary: Updates a guest
tags:
- guests
/api/v1/guests/search:
post:
consumes:
- application/json
description: Retrieves guests optionally filtered by floor
parameters:
- description: Hotel ID (UUID)
in: header
name: X-Hotel-ID
required: true
type: string
- description: Guest filters
in: body
name: body
required: true
schema:
$ref: '#/definitions/GuestFilters'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/GuestPage'
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
security:
- BearerAuth: []
summary: Get Guests
tags:
- guests
/api/v1/guests/stays/{id}:
get:
consumes:
Expand Down
2 changes: 1 addition & 1 deletion backend/internal/handler/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (h *GuestsHandler) UpdateGuest(c *fiber.Ctx) error {
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Security BearerAuth
// @Router /api/v1/guests [post]
// @Router /api/v1/guests/search [post]
func (h *GuestsHandler) GetGuests(c *fiber.Ctx) error {
hotelID := c.Get("X-Hotel-ID")
var filters models.GuestFilters
Expand Down
16 changes: 8 additions & 8 deletions backend/internal/models/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type Guest struct {
} //@name Guest

type GuestFilters struct {
HotelID string `json:"hotel_id" validate:"required,uuid"`
HotelID string `json:"hotel_id" validate:"required,uuid" swaggerignore:"true"`
Floors []int `json:"floors"`
GroupSize []int `json:"group_size"`
Search string `json:"search"`
Expand All @@ -59,13 +59,13 @@ type GuestPage struct {
} // @name GuestPage

type GuestWithBooking struct {
ID string `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PreferredName string `json:"preferred_name"`
Floor int `json:"floor"`
RoomNumber int `json:"room_number"`
GroupSize *int `json:"group_size"`
ID string `json:"id" validate:"required" example:"530e8400-e458-41d4-a716-446655440000"`
FirstName string `json:"first_name" validate:"required" example:"Jane"`
LastName string `json:"last_name" validate:"required" example:"Doe"`
PreferredName string `json:"preferred_name" validate:"required" example:"Jane"`
Floor int `json:"floor" validate:"required" example:"3"`
RoomNumber int `json:"room_number" validate:"required" example:"301"`
GroupSize *int `json:"group_size" example:"2"`
} // @name GuestWithBooking

type GuestWithStays struct {
Expand Down
5 changes: 4 additions & 1 deletion backend/internal/repository/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ func (r *GuestsRepository) FindGuestWithStayHistory(ctx context.Context, id stri
var status *models.BookingStatus

if guest == nil {
guest = &models.GuestWithStays{}
guest = &models.GuestWithStays{
CurrentStays: []models.Stay{},
PastStays: []models.Stay{},
}
}

err := rows.Scan(
Expand Down
10 changes: 10 additions & 0 deletions clients/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,18 @@ export {
usePostApiV1Guests,
useGetApiV1GuestsId,
usePutApiV1GuestsId,
usePostApiV1GuestsSearchHook,
useGetApiV1GuestsStaysId,
} from "./api/generated/endpoints/guests/guests";

export type {
GuestWithBooking,
GuestWithStays,
GuestPage,
GuestFilters,
Stay,
} from "./api/generated/models";

export { usePostRooms, useGetRoomsFloors } from "./api/generated/endpoints/rooms/rooms";

export type {
Expand Down
53 changes: 53 additions & 0 deletions clients/web/src/components/guests/GuestDetailsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { GuestDrawerTab } from "./guest-drawer-state";
import { DrawerShell } from "@/components/ui/DrawerShell";

type GuestDetailsDrawerProps = {
guestName: string;
activeTab: GuestDrawerTab;
onChangeTab: (tab: GuestDrawerTab) => void;
onClose: () => void;
children: React.ReactNode;
};

const tabs: Array<{ label: string; value: GuestDrawerTab }> = [
{ label: "Profile", value: "profile" },
{ label: "Visit Activity", value: "activity" },
];

export function GuestDetailsDrawer({
guestName,
activeTab,
onChangeTab,
onClose,
children,
}: GuestDetailsDrawerProps) {
return (
<DrawerShell title={guestName} onClose={onClose}>
<div className="border-b border-stroke-subtle">
<div className="grid grid-cols-2">
{tabs.map((tab) => {
const selected = tab.value === activeTab;

return (
<button
key={tab.value}
type="button"
aria-pressed={selected}
className={`border-b-2 px-4 py-3 text-sm ${
selected
? "border-primary text-primary"
: "border-transparent text-text-subtle"
}`}
onClick={() => onChangeTab(tab.value)}
>
{tab.label}
</button>
);
})}
</div>
</div>

{children}
</DrawerShell>
);
}
Loading
Loading