Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions clients/mobile/components/tasks/priority-tag.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Feather from "@expo/vector-icons/Feather";
import { Text, View } from "react-native";

type Priority = "high" | "medium" | "low";
import type { RequestPriority } from "@shared";

type PriorityConfig = {
containerClass: string;
Expand All @@ -10,7 +9,7 @@ type PriorityConfig = {
iconColor: string;
};

const PRIORITY_CONFIG: Record<Priority, PriorityConfig> = {
const PRIORITY_CONFIG: Record<RequestPriority, PriorityConfig> = {
high: {
containerClass: "bg-priority-high-bg",
textClass: "text-priority-high",
Expand Down Expand Up @@ -38,7 +37,8 @@ type PriorityTagProps = {

export function PriorityTag({ priority, dimmed = false }: PriorityTagProps) {
const config =
PRIORITY_CONFIG[priority.toLowerCase() as Priority] ?? PRIORITY_CONFIG.low;
PRIORITY_CONFIG[priority.toLowerCase() as RequestPriority] ??
PRIORITY_CONFIG.low;

return (
<View
Expand Down
2 changes: 1 addition & 1 deletion clients/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type {
} from "./api/profile-picture";

// Generated Types - Models
export { MakeRequestPriority } from "./api/generated/models";
export { MakeRequestPriority, RequestPriority } from "./api/generated/models";

export type {
User,
Expand Down
67 changes: 67 additions & 0 deletions clients/web/src/components/guests/ActiveBookingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { CalendarDays, UsersRound } from "lucide-react";
import type { Stay } from "@shared";
import { cn } from "@/lib/utils";
import { formatDate } from "@/utils/dates";

type ActiveBookingCardProps = {
stay: Stay;
compact?: boolean;
};

export function ActiveBookingCard({ stay, compact }: ActiveBookingCardProps) {
return (
<div
className={cn(
"flex flex-col gap-2 rounded-lg border border-primary bg-bg-selected p-4",
compact && "w-[231px] shrink-0",
)}
>
<div className="flex items-start justify-between">
<span className="text-xl font-bold text-primary">
Suite {stay.room_number}
</span>
{stay.group_size != null && (
<div className="flex items-center gap-1 text-primary">
<UsersRound className="size-[19px]" strokeWidth={1.5} />
<span className="text-xl font-bold">{stay.group_size}</span>
</div>
)}
</div>
{compact ? (
<div className="flex flex-col gap-1">
<div className="flex flex-col gap-1">
<span className="text-sm font-medium text-primary">Arrival:</span>
<div className="flex items-center gap-2 text-sm text-primary">
<CalendarDays className="size-3 shrink-0" strokeWidth={1.5} />
<span>{formatDate(stay.arrival_date)}</span>
</div>
</div>
<div className="flex flex-col gap-1">
<span className="text-sm font-medium text-primary">Departure:</span>
<div className="flex items-center gap-2 text-sm text-primary">
<CalendarDays className="size-3 shrink-0" strokeWidth={1.5} />
<span>{formatDate(stay.departure_date)}</span>
</div>
</div>
</div>
) : (
<div className="flex gap-[72px]">
<div className="flex flex-1 flex-col gap-1">
<span className="text-sm font-medium text-primary">Arrival:</span>
<div className="flex items-center gap-2 text-sm text-primary">
<CalendarDays className="size-3 shrink-0" strokeWidth={1.5} />
<span>{formatDate(stay.arrival_date)}</span>
</div>
</div>
<div className="flex flex-1 flex-col gap-1">
<span className="text-sm font-medium text-primary">Departure:</span>
<div className="flex items-center gap-2 text-sm text-primary">
<CalendarDays className="size-3 shrink-0" strokeWidth={1.5} />
<span>{formatDate(stay.departure_date)}</span>
</div>
</div>
</div>
)}
</div>
);
}
74 changes: 74 additions & 0 deletions clients/web/src/components/guests/GuestBookingHistoryView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ChevronRight } from "lucide-react";
import { ActiveBookingCard } from "./ActiveBookingCard";
import { PastBookingCard } from "./PastBookingCard";
import type { Stay } from "@shared";

type GuestBookingHistoryViewProps = {
currentStays: Array<Stay>;
pastStays: Array<Stay>;
onBack: () => void;
};

export function GuestBookingHistoryView({
currentStays,
pastStays,
onBack,
}: GuestBookingHistoryViewProps) {
const byYear = pastStays.reduce<Record<string, Array<Stay>>>((acc, stay) => {
const year = stay.arrival_date.slice(0, 4);
(acc[year] ??= []).push(stay);
return acc;
}, {});
const years = Object.keys(byYear).sort((a, b) => Number(b) - Number(a));

return (
<div className="flex flex-col gap-6 p-6">
{/* Breadcrumb */}
<div className="flex items-center gap-1 text-base">
<button
type="button"
aria-label="Visit Activity"
onClick={onBack}
className="text-text-default hover:underline"
>
Visit Activity
</button>
<ChevronRight className="size-3.5 text-text-default" strokeWidth={2} />
<span className="text-text-default">Booking History</span>
</div>

{/* Active stays */}
{currentStays.length > 0 && (
<section className="flex flex-col gap-4">
<h3 className="text-base font-medium text-text-default">
Active Bookings ({currentStays.length})
</h3>
<div className="flex flex-col gap-3">
{currentStays.map((stay) => (
<ActiveBookingCard
key={`active-${stay.room_number}`}
stay={stay}
/>
))}
</div>
</section>
)}

{/* Past stays by year */}
{years.length > 0 ? (
years.map((year) => (
<section key={year} className="flex flex-col gap-2">
<h3 className="text-sm font-medium text-text-default">{year}</h3>
<div className="flex flex-col gap-2">
{byYear[year].map((stay, i) => (
<PastBookingCard key={`${year}-${i}`} stay={stay} />
))}
</div>
</section>
))
) : (
<p className="text-sm text-text-subtle">No booking history.</p>
)}
</div>
);
}
15 changes: 13 additions & 2 deletions clients/web/src/components/guests/GuestDetailsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { X } from "lucide-react";
import { useGetGuestsStaysId, usePutGuestsId } from "@shared";
import {
useGetGuestsStaysId,
useGetRequestGuestId,
usePutGuestsId,
} from "@shared";
import { GuestProfileTab } from "./GuestProfileTab";
import { GuestVisitActivityTab } from "./GuestVisitActivityTab";
import { cn } from "@/lib/utils";
import { Skeleton } from "@/components/ui/skeleton";

Expand Down Expand Up @@ -33,6 +38,8 @@ export function GuestDetailsDrawer({
isError,
refetch,
} = useGetGuestsStaysId(guestId);
const { data: requestsData } = useGetRequestGuestId(guestId);
const requests = (requestsData as any)?.items ?? requestsData ?? [];
const updateGuest = usePutGuestsId();

const handleSaveNotes = async (notes: string) => {
Expand Down Expand Up @@ -112,7 +119,11 @@ export function GuestDetailsDrawer({
/>
)}
{activeTab === GuestDrawerTab.Activity && (
<div className="p-6 text-sm text-text-subtle">Coming soon.</div>
<GuestVisitActivityTab
currentStays={guest.current_stays}
Comment thread
Dao-Ho marked this conversation as resolved.
pastStays={guest.past_stays}
requests={requests}
/>
)}
</>
)}
Expand Down
93 changes: 93 additions & 0 deletions clients/web/src/components/guests/GuestVisitActivityTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState } from "react";
import { ChevronRight } from "lucide-react";
import { ActiveBookingCard } from "./ActiveBookingCard";
import { GuestBookingHistoryView } from "./GuestBookingHistoryView";
import { PastBookingCard } from "./PastBookingCard";
import { RequestCard } from "./RequestCard";
import type { GuestRequest, Stay } from "@shared";

type GuestVisitActivityTabProps = {
currentStays: Array<Stay>;
pastStays: Array<Stay>;
requests: Array<GuestRequest>;
};

export function GuestVisitActivityTab({
currentStays,
pastStays,
requests,
}: GuestVisitActivityTabProps) {
const [showHistory, setShowHistory] = useState(false);

if (showHistory) {
return (
<GuestBookingHistoryView
currentStays={currentStays}
pastStays={pastStays}
onBack={() => setShowHistory(false)}
/>
);
}

const hasActiveBookings = currentStays.length > 0;

return (
<div className="flex flex-col gap-6 p-6">
{/* Active Bookings or Previous Bookings */}
<section className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<h3 className="text-base font-medium text-text-default">
{hasActiveBookings
? `Active Bookings (${currentStays.length})`
: "Previous Bookings"}
</h3>
<button
type="button"
aria-label="View all bookings"
onClick={() => setShowHistory(true)}
className="flex items-center gap-1 text-base text-text-default hover:underline"
>
View All Bookings
<ChevronRight className="size-3.5" strokeWidth={2} />
</button>
</div>

{hasActiveBookings ? (
currentStays.length === 1 ? (
<ActiveBookingCard stay={currentStays[0]} />
) : (
<div className="flex gap-5">
{currentStays.slice(0, 2).map((stay) => (
<ActiveBookingCard key={stay.room_number} stay={stay} compact />
))}
</div>
)
) : pastStays.length > 0 ? (
<div className="flex flex-col gap-2">
{pastStays.slice(0, 3).map((stay, i) => (
<PastBookingCard key={`past-${i}`} stay={stay} />
))}
</div>
) : (
<p className="text-sm text-text-subtle">No bookings.</p>
)}
</section>

{/* Requests */}
<section className="flex flex-col gap-4">
<h3 className="text-base font-medium text-text-default">
{requests.length > 0 ? `Requests (${requests.length})` : "Requests"}
</h3>
{requests.length > 0 ? (
<div className="flex flex-col gap-4">
{requests.map((req) => (
<RequestCard key={req.id ?? req.name} req={req} />
))}
</div>
) : (
<p className="text-base text-text-secondary">You're all caught up!</p>
)}
</section>
</div>
);
}
30 changes: 30 additions & 0 deletions clients/web/src/components/guests/PastBookingCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { UsersRound } from "lucide-react";
import type { Stay } from "@shared";
import { formatDate } from "@/utils/dates";

type PastBookingCardProps = {
stay: Stay;
};

export function PastBookingCard({ stay }: PastBookingCardProps) {
return (
<div className="flex items-center rounded-lg border border-text-subtle bg-bg-container p-4">
<div className="flex flex-1 flex-col gap-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-text-subtle">
Suite {stay.room_number}
</span>
{stay.group_size != null && (
<div className="flex items-center gap-0.5 text-text-subtle">
<UsersRound className="size-3" strokeWidth={1.5} />
<span className="text-sm font-medium">{stay.group_size}</span>
</div>
)}
</div>
<span className="text-sm text-text-subtle">
{formatDate(stay.arrival_date)} - {formatDate(stay.departure_date)}
</span>
</div>
</div>
);
}
Loading
Loading