refactor: create a reusable component for dashboard tool cards

This commit is contained in:
JasterV 2026-02-03 16:56:07 +01:00
parent ee1232d829
commit 3239ef0bd9
5 changed files with 93 additions and 118 deletions

View file

@ -0,0 +1,65 @@
defmodule SpazioSolazzoWeb.AdminDashboardComponents do
@moduledoc """
Reusable components for the booking flow.
"""
use Phoenix.Component
import SpazioSolazzoWeb.CoreComponents, only: [icon: 1]
attr :title, :string, required: true
attr :description, :string, required: true
attr :color, :atom, values: [:primary, :secondary], required: true
attr :icon, :string, required: true
attr :navigate, :string, required: true
@doc """
Renders a tool card to be displayed in the admin dashboard
"""
def tool_card(assigns) do
~H"""
<.link
navigate={@navigate}
class={"group bg-white dark:bg-slate-800 rounded-3xl p-8 border-2 border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none #{container_color_class(@color)} transition-all duration-300 hover:scale-[1.02]"}
>
<div class="flex flex-col h-full">
<div class="flex items-start justify-between mb-6">
<div class={"size-16 rounded-2xl #{icon_color_class(@color)} flex items-center justify-center group-hover:scale-110 transition-transform duration-300"}>
<.icon name={@icon} class="w-8 h-8" />
</div>
</div>
<h2 class={"text-2xl font-bold text-slate-900 dark:text-white mb-3 #{title_color_class(@color)} transition-colors"}>
{@title}
</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6 flex-grow">
{@description}
</p>
<div class={"flex items-center #{tooltip_color_class(@color)} font-semibold group-hover:gap-3 transition-all"}>
<span>Open Tool</span>
<.icon
name="hero-arrow-right"
class="w-5 h-5 group-hover:translate-x-1 transition-transform"
/>
</div>
</div>
</.link>
"""
end
defp container_color_class(:primary), do: "hover:border-primary dark:hover:border-primary"
defp container_color_class(:secondary), do: "hover:border-sky-500 dark:hover:border-sky-400"
defp icon_color_class(:primary),
do: "bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400"
defp icon_color_class(:secondary),
do: "bg-sky-100 dark:bg-sky-900/30 text-sky-600 dark:text-sky-400 "
defp title_color_class(:primary), do: "group-hover:text-primary dark:group-hover:text-primary"
defp title_color_class(:secondary), do: "group-hover:text-sky-500 dark:group-hover:text-sky-400"
defp tooltip_color_class(:primary), do: "text-primary dark:text-primary-hover"
defp tooltip_color_class(:secondary), do: "text-sky-500 dark:text-sky-400"
end

View file

@ -1,15 +1,10 @@
<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">
<%!-- Header with back button --%>
<div class="flex items-center gap-4">
<.link
navigate="/admin/dashboard"
class="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-primary dark:text-slate-400 dark:hover:text-primary transition-colors"
>
<.icon name="hero-arrow-left" class="w-5 h-5" /> Back to Dashboard
</.link>
</div>
<.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">

View file

@ -5,16 +5,9 @@ defmodule SpazioSolazzoWeb.Admin.DashboardLive do
use SpazioSolazzoWeb, :live_view
def mount(_params, _session, socket) do
# Get pending requests count directly from database (no data loaded)
{:ok, pending_count} =
Ash.count(SpazioSolazzo.BookingSystem.Booking,
query: [filter: [state: :requested]]
)
import SpazioSolazzoWeb.AdminDashboardComponents
{:ok,
assign(socket,
pending_requests_count: pending_count
)}
def mount(_params, _session, socket) do
{:ok, socket}
end
end

View file

@ -6,97 +6,26 @@
Admin Dashboard
</h1>
<p class="text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
Welcome to Spazio Solazzo management center. Choose a tool below to get started.
Welcome to Spazio Solazzo management center.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<%!-- Booking Management Tool Card --%>
<.link
navigate="/admin/bookings"
class="group bg-white dark:bg-slate-800 rounded-3xl p-8 border-2 border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none hover:border-primary dark:hover:border-primary transition-all duration-300 hover:scale-[1.02]"
>
<div class="flex flex-col h-full">
<div class="flex items-start justify-between mb-6">
<div class="size-16 rounded-2xl bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<.icon name="hero-clipboard-document-list" class="w-8 h-8" />
</div>
<%= if @pending_requests_count > 0 do %>
<span class="bg-amber-100 dark:bg-amber-900/40 text-amber-800 dark:text-amber-200 text-xs font-bold px-3 py-1 rounded-full flex items-center gap-1">
<.icon name="hero-clock" class="w-3.5 h-3.5" />
{@pending_requests_count} pending
</span>
<% end %>
</div>
<.tool_card
title="Booking Management"
description="Review and manage pending booking requests. Approve or reject reservations and view booking history."
color={:primary}
icon="hero-clipboard-document-list"
navigate={~p"/admin/bookings"}
/>
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-3 group-hover:text-primary dark:group-hover:text-primary transition-colors">
Booking Management
</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6 flex-grow">
Review and manage pending booking requests. Approve or reject reservations and view booking history.
</p>
<div class="flex items-center text-primary dark:text-primary-hover font-semibold group-hover:gap-3 transition-all">
<span>Open Tool</span>
<.icon
name="hero-arrow-right"
class="w-5 h-5 group-hover:translate-x-1 transition-transform"
/>
</div>
</div>
</.link>
<%!-- Walk-in Booking Tool Card --%>
<.link
navigate="/admin/walk-in"
class="group bg-white dark:bg-slate-800 rounded-3xl p-8 border-2 border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none hover:border-sky-500 dark:hover:border-sky-400 transition-all duration-300 hover:scale-[1.02]"
>
<div class="flex flex-col h-full">
<div class="flex items-start justify-between mb-6">
<div class="size-16 rounded-2xl bg-sky-100 dark:bg-sky-900/30 text-sky-600 dark:text-sky-400 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<.icon name="hero-user-plus" class="w-8 h-8" />
</div>
<span class="bg-sky-100 dark:bg-sky-900/40 text-sky-800 dark:text-sky-200 text-xs font-bold px-3 py-1 rounded-full flex items-center gap-1">
<.icon name="hero-building-office-2" class="w-3.5 h-3.5" /> Coworking Only
</span>
</div>
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-3 group-hover:text-sky-500 dark:group-hover:text-sky-400 transition-colors">
Walk-in Booking
</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6 flex-grow">
Create instant bookings for walk-in customers at the coworking space. View real-time availability calendar.
</p>
<div class="flex items-center text-sky-500 dark:text-sky-400 font-semibold group-hover:gap-3 transition-all">
<span>Open Tool</span>
<.icon
name="hero-arrow-right"
class="w-5 h-5 group-hover:translate-x-1 transition-transform"
/>
</div>
</div>
</.link>
</div>
<div class="mt-12 p-6 bg-slate-50 dark:bg-slate-800/50 rounded-2xl border border-slate-200 dark:border-slate-700">
<div class="flex items-start gap-4">
<div class="shrink-0 text-slate-400 dark:text-slate-500">
<.icon name="hero-information-circle" class="w-6 h-6" />
</div>
<div>
<h3 class="text-sm font-bold text-slate-900 dark:text-white mb-1">
Quick Tips
</h3>
<ul class="text-sm text-slate-600 dark:text-slate-400 space-y-1">
<li>• Booking requests require approval before confirmation</li>
<li>• Walk-in bookings are instantly approved for coworking space</li>
<li>• All bookings are paid upon customer arrival at reception</li>
</ul>
</div>
</div>
<.tool_card
title="Arcipelago Walk-in Booking"
description="Create instant bookings for walk-in customers at the coworking space."
color={:secondary}
icon="hero-building-office-2"
navigate={~p"/admin/walk-in"}
/>
</div>
</div>
</main>

View file

@ -1,15 +1,10 @@
<Layouts.app flash={@flash} current_user={@current_user}>
<main class="flex-1">
<section class="mx-auto max-w-[1000px] px-6 py-10">
<%!-- Header with back button --%>
<div class="mb-6">
<.link
navigate="/admin/dashboard"
class="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-primary dark:text-slate-400 dark:hover:text-primary transition-colors"
>
<.icon name="hero-arrow-left" class="w-5 h-5" /> Back to Dashboard
</.link>
</div>
<.back_to_link
navigate={~p"/admin/dashboard"}
value="Back to Dashboard"
/>
<%!-- Title --%>
<div class="mb-10 flex flex-col items-center md:items-start text-center md:text-left">
@ -22,7 +17,7 @@
</div>
<div class="space-y-8">
<%!-- Step 1: Date & Time --%>
<%!-- Date & Time --%>
<article class="bg-white dark:bg-slate-800 rounded-3xl p-6 md:p-8 border border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none transition-all hover:border-primary/50 dark:hover:border-primary/50 group">
<header class="flex flex-col md:flex-row md:items-center gap-4 mb-8 pb-6 border-b border-slate-100 dark:border-slate-700/50">
<div class="size-12 rounded-2xl bg-primary text-white flex items-center justify-center text-xl font-bold shadow-lg shadow-primary/30 group-hover:scale-110 transition-transform duration-300">
@ -171,7 +166,7 @@
</div>
</article>
<%!-- Step 2: Customer Details --%>
<%!-- Customer Details --%>
<article class="bg-white dark:bg-slate-800 rounded-3xl p-6 md:p-8 border border-slate-200 dark:border-slate-700 shadow-xl shadow-slate-200/50 dark:shadow-none transition-all hover:border-primary/50 dark:hover:border-primary/50 group">
<header class="flex flex-col md:flex-row md:items-center gap-4 mb-8 pb-6 border-b border-slate-100 dark:border-slate-700/50">
<div class="size-12 rounded-2xl bg-primary text-white flex items-center justify-center text-xl font-bold shadow-lg shadow-primary/30 group-hover:scale-110 transition-transform duration-300">
@ -234,7 +229,6 @@
</.form>
</article>
<%!-- Submit button --%>
<div class="flex justify-center pt-4">
<.button
type="submit"
@ -242,7 +236,6 @@
class="w-full sm:w-auto flex items-center justify-center gap-2 overflow-hidden rounded-xl h-12 px-10 bg-primary hover:bg-primary/90 transition-colors text-white text-base font-bold shadow-lg shadow-primary/30"
>
<span>Create Booking</span>
<.icon name="hero-check" class="w-5 h-5" />
</.button>
</div>
</div>