mirror of
https://codeberg.org/JasterV/spazio-solazzo.git
synced 2026-04-26 18:20:03 +00:00
196 lines
8.4 KiB
Text
196 lines
8.4 KiB
Text
<Layouts.app flash={@flash} current_user={@current_user}>
|
|
<main class="flex-grow px-4 py-8 md:px-8">
|
|
<div class="max-w-6xl mx-auto flex flex-col gap-8">
|
|
<.back_to_link
|
|
navigate={~p"/admin/dashboard"}
|
|
value="Back to Dashboard"
|
|
/>
|
|
|
|
<%!-- Title and stats --%>
|
|
<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
|
<div class="flex flex-col gap-2">
|
|
<h1 class="text-slate-900 dark:text-white tracking-tight text-3xl md:text-4xl font-extrabold">
|
|
Manage Bookings
|
|
</h1>
|
|
<p class="text-slate-600 dark:text-slate-400 text-base font-normal max-w-2xl">
|
|
Review reservations and booking history. Pending requests require approval.
|
|
</p>
|
|
</div>
|
|
<div class="flex gap-4">
|
|
<div class="bg-white dark:bg-slate-800 px-5 py-3 rounded-xl border border-slate-200 dark:border-slate-700 shadow-sm flex flex-col items-start min-w-[140px]">
|
|
<span class="text-xs font-bold uppercase tracking-wider text-slate-600 dark:text-slate-400">
|
|
Pending
|
|
</span>
|
|
<span class="text-2xl font-bold text-primary">{@pending_page.count}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<%!-- Filters --%>
|
|
<div class="bg-white dark:bg-slate-800 p-5 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700">
|
|
<form
|
|
phx-change="filter_bookings"
|
|
phx-submit="filter_bookings"
|
|
class="grid grid-cols-1 md:grid-cols-12 gap-4 items-end"
|
|
>
|
|
<div class="col-span-1 md:col-span-4 flex flex-col gap-1.5">
|
|
<label class="text-sm font-semibold text-slate-900 dark:text-slate-200 ml-1">
|
|
Customer Email
|
|
</label>
|
|
<div class="relative">
|
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500">
|
|
<.icon name="hero-magnifying-glass" class="w-5 h-5" />
|
|
</span>
|
|
<input
|
|
name="email"
|
|
value={@filter_email}
|
|
class="w-full h-12 pl-10 pr-4 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent outline-none transition-all placeholder:text-slate-400"
|
|
placeholder="Search by email..."
|
|
type="text"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-3 flex flex-col gap-1.5">
|
|
<label class="text-sm font-semibold text-slate-900 dark:text-slate-200 ml-1">
|
|
Space
|
|
</label>
|
|
<div class="relative">
|
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500 pointer-events-none">
|
|
<.icon name="hero-map-pin" class="w-5 h-5" />
|
|
</span>
|
|
<select
|
|
name="space"
|
|
class="w-full h-12 pl-10 pr-10 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent outline-none appearance-none cursor-pointer"
|
|
>
|
|
<option value="">All Spaces</option>
|
|
<%= for space <- @spaces do %>
|
|
<option value={space.slug} selected={@filter_space == space.slug}>
|
|
{space.name}
|
|
</option>
|
|
<% end %>
|
|
</select>
|
|
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-slate-500 pointer-events-none">
|
|
<.icon name="hero-chevron-down" class="w-5 h-5" />
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-3 flex flex-col gap-1.5">
|
|
<label class="text-sm font-semibold text-slate-900 dark:text-slate-200 ml-1">
|
|
Date
|
|
</label>
|
|
<div class="relative">
|
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500 pointer-events-none">
|
|
<.icon name="hero-calendar" class="w-5 h-5" />
|
|
</span>
|
|
<input
|
|
name="date"
|
|
value={if @filter_date, do: Date.to_iso8601(@filter_date), else: ""}
|
|
class="w-full h-12 pl-10 pr-4 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-white focus:ring-2 focus:ring-primary focus:border-transparent outline-none cursor-pointer"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-2">
|
|
<button
|
|
type="button"
|
|
phx-click="clear_filters"
|
|
class="w-full h-12 bg-slate-900 dark:bg-white hover:bg-slate-800 dark:hover:bg-slate-100 text-white dark:text-slate-900 font-bold rounded-xl transition-colors flex items-center justify-center gap-2 cursor-pointer"
|
|
>
|
|
<span>Clear Filters</span>
|
|
<.icon name="hero-x-mark" class="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<%= if @pending_page.count == 0 && @history_page.count == 0 do %>
|
|
<div class="text-center py-12 bg-slate-50 dark:bg-slate-800/50 rounded-xl">
|
|
<.icon name="hero-inbox" class="w-16 h-16 text-slate-400 mx-auto mb-4" />
|
|
<p class="text-slate-500 dark:text-slate-400 text-lg">No bookings found</p>
|
|
</div>
|
|
<% end %>
|
|
|
|
<%!-- Pending Bookings Table --%>
|
|
<%= if @pending_page.count > 0 do %>
|
|
<.bookings_table
|
|
title="Pending Requests"
|
|
bookings={@pending_page.results}
|
|
page={@pending_page}
|
|
current_page={@pending_page_number}
|
|
event_prefix="pending"
|
|
expanded_booking_ids={@expanded_booking_ids}
|
|
show_actions={true}
|
|
show_cancellation_details={false}
|
|
/>
|
|
<% end %>
|
|
|
|
<%!-- Past Bookings Table --%>
|
|
<%= if @history_page.count > 0 do %>
|
|
<.bookings_table
|
|
title="Booking History"
|
|
bookings={@history_page.results}
|
|
page={@history_page}
|
|
current_page={@history_page_number}
|
|
event_prefix="history"
|
|
expanded_booking_ids={@expanded_booking_ids}
|
|
show_actions={false}
|
|
show_cancellation_details={true}
|
|
/>
|
|
<% end %>
|
|
</div>
|
|
</main>
|
|
|
|
<%!-- Reject Modal --%>
|
|
<%= if @show_reject_modal do %>
|
|
<div
|
|
class="fixed inset-0 bg-slate-900/20 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
|
phx-click="hide_reject_modal"
|
|
>
|
|
<div
|
|
class="bg-white dark:bg-slate-800 rounded-3xl border border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none max-w-md w-full p-6"
|
|
phx-click="stop_propagation"
|
|
>
|
|
<h3 class="text-xl font-bold text-slate-900 dark:text-white mb-3">Reject Booking</h3>
|
|
|
|
<p class="text-slate-600 dark:text-slate-400 mb-4 text-sm">
|
|
Provide a reason for rejecting this booking. The customer will receive this in their email.
|
|
</p>
|
|
|
|
<form phx-submit="confirm_reject">
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate-900 dark:text-white mb-2">
|
|
Rejection Reason <span class="text-red-500">*</span>
|
|
</label>
|
|
<textarea
|
|
phx-change="update_rejection_reason"
|
|
name="reason"
|
|
rows="4"
|
|
required
|
|
placeholder="e.g., Space under maintenance, Fully booked, etc."
|
|
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-white rounded-xl focus:outline-none focus:ring-2 focus:ring-primary"
|
|
>{@rejection_reason}</textarea>
|
|
</div>
|
|
|
|
<div class="flex gap-2">
|
|
<button
|
|
type="button"
|
|
phx-click="hide_reject_modal"
|
|
class="flex-1 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-200 px-4 py-2 rounded-lg font-medium hover:bg-slate-300 dark:hover:bg-slate-600 transition-colors text-sm"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="flex-1 bg-red-500 text-white px-4 py-2 rounded-lg font-medium hover:bg-red-600 transition-colors text-sm"
|
|
>
|
|
Reject
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
</Layouts.app>
|