From 123cae65c22912023b8e6635f42c254e1098c6eb Mon Sep 17 00:00:00 2001 From: Groom Book CTO Date: Thu, 19 Mar 2026 03:00:23 +0000 Subject: [PATCH] =?UTF-8?q?Improve=20admin=20UI=20visual=20design=20?= =?UTF-8?q?=E2=80=94=20polish=20look=20and=20feel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sticky nav bar with subtle shadow, branded GroomBook wordmark, green gradient Book button - Consistent brand green (#4f8a6f) for primary buttons across all admin pages - Tables wrapped in white cards with rounded corners and soft shadows - Uppercase table headers with better spacing and hierarchy - Input/button border-radius increased to 6px for softer feel - Global CSS: button transitions, input focus states with brand green ring, subtle card shadows - Background changed from plain white to light gray (#f0f2f5) for depth - Reports: polished stat cards with shadows, refined section headers, card-wrapped tables - Custom scrollbar styling for a cleaner look Closes groombook/groombook#58 Co-Authored-By: Paperclip --- apps/web/src/App.tsx | 41 +++++++++------ apps/web/src/index.css | 50 ++++++++++++++++++- apps/web/src/pages/Appointments.tsx | 21 ++++---- apps/web/src/pages/Clients.tsx | 12 ++--- apps/web/src/pages/GroupBooking.tsx | 19 +++---- apps/web/src/pages/Invoices.tsx | 18 ++++--- apps/web/src/pages/Reports.tsx | 77 +++++++++++++++-------------- apps/web/src/pages/Services.tsx | 18 ++++--- apps/web/src/pages/Staff.tsx | 14 +++--- 9 files changed, 170 insertions(+), 100 deletions(-) diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index b3ef7fc..7df2c75 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -23,29 +23,42 @@ const NAV_LINKS = [ function AdminLayout() { const location = useLocation(); return ( -
+
-
+
} /> } /> diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 67a2b22..6d09d02 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -10,11 +10,13 @@ body { font-size: 16px; line-height: 1.5; color: #1a202c; - background: #f7fafc; + background: #f0f2f5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } a { - color: #4f8a6f; + color: #3d7a5f; text-decoration: none; } @@ -25,4 +27,48 @@ a:hover { h1 { font-size: 1.5rem; margin-top: 0; + letter-spacing: -0.01em; +} + +h2, h3, h4 { + letter-spacing: -0.01em; +} + +/* ─── Admin button polish ─── */ +button { + transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease; +} + +button:active:not(:disabled) { + transform: translateY(0.5px); +} + +/* ─── Admin input / select focus states ─── */ +input:focus, select:focus, textarea:focus { + outline: none; + border-color: #4f8a6f; + box-shadow: 0 0 0 3px rgba(79, 138, 111, 0.12); +} + +/* ─── Admin card-like containers (borders get subtle shadow) ─── */ +[style*="border: 1px solid"] { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); +} + +/* ─── Scrollbar polish ─── */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #94a3b8; } diff --git a/apps/web/src/pages/Appointments.tsx b/apps/web/src/pages/Appointments.tsx index 20c5fae..ea2c539 100644 --- a/apps/web/src/pages/Appointments.tsx +++ b/apps/web/src/pages/Appointments.tsx @@ -291,7 +291,7 @@ export function AppointmentsPage() { @@ -370,11 +370,11 @@ export function AppointmentsPage() { {days.map((day, i) => { const isToday = formatDate(day) === formatDate(new Date()); return ( -
+
{saving ? "Saving…" @@ -841,19 +841,20 @@ function Field({ label, children }: { label: string; children: React.ReactNode } } const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", - borderRadius: 4, - background: "#f9fafb", + borderRadius: 6, + background: "#fff", cursor: "pointer", fontSize: 13, + fontWeight: 500, }; const inputStyle: React.CSSProperties = { width: "100%", - padding: "0.4rem 0.5rem", + padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", - borderRadius: 4, + borderRadius: 6, fontSize: 14, boxSizing: "border-box", }; diff --git a/apps/web/src/pages/Clients.tsx b/apps/web/src/pages/Clients.tsx index e28d424..50df051 100644 --- a/apps/web/src/pages/Clients.tsx +++ b/apps/web/src/pages/Clients.tsx @@ -315,7 +315,7 @@ export function ClientsPage() {

Clients

@@ -387,7 +387,7 @@ export function ClientsPage() { ) : (
{pets.map((p) => ( -
+
{p.name}
@@ -498,7 +498,7 @@ export function ClientsPage() { {clientFormError &&

{clientFormError}

}
- @@ -637,7 +637,7 @@ export function ClientsPage() { {logFormError &&

{logFormError}

}
- @@ -674,9 +674,9 @@ function Field({ label, children }: { label: string; children: React.ReactNode } } const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", border: "1px solid #d1d5db", borderRadius: 4, background: "#f9fafb", cursor: "pointer", fontSize: 13, + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", borderRadius: 6, background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 500, }; const inputStyle: React.CSSProperties = { - width: "100%", padding: "0.4rem 0.5rem", border: "1px solid #d1d5db", borderRadius: 4, fontSize: 14, boxSizing: "border-box", + width: "100%", padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", borderRadius: 6, fontSize: 14, boxSizing: "border-box", }; diff --git a/apps/web/src/pages/GroupBooking.tsx b/apps/web/src/pages/GroupBooking.tsx index 8662a07..445530a 100644 --- a/apps/web/src/pages/GroupBooking.tsx +++ b/apps/web/src/pages/GroupBooking.tsx @@ -287,7 +287,7 @@ function NewGroupBookingForm({ @@ -471,7 +471,7 @@ export function GroupBookingPage() { @@ -558,25 +558,26 @@ function Field({ } const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", - borderRadius: 4, - background: "#f9fafb", + borderRadius: 6, + background: "#fff", cursor: "pointer", fontSize: 13, + fontWeight: 500, }; const inputStyle: React.CSSProperties = { width: "100%", - padding: "0.4rem 0.5rem", + padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", - borderRadius: 4, + borderRadius: 6, fontSize: 13, boxSizing: "border-box", }; const tdStyle: React.CSSProperties = { - padding: "0.45rem 1rem", - borderBottom: "1px solid #f1f5f9", + padding: "0.5rem 1rem", + borderBottom: "1px solid #f3f4f6", color: "#374151", }; diff --git a/apps/web/src/pages/Invoices.tsx b/apps/web/src/pages/Invoices.tsx index dd86cf7..d1c3457 100644 --- a/apps/web/src/pages/Invoices.tsx +++ b/apps/web/src/pages/Invoices.tsx @@ -129,7 +129,7 @@ function CreateFromAppointmentForm({ @@ -540,7 +540,7 @@ export function InvoicesPage() { @@ -551,11 +551,12 @@ export function InvoicesPage() { No invoices yet. Create one from a completed appointment.

) : ( +
{["Date", "Client", "Subtotal", "Tax", "Tip", "Total", "Status", ""].map((h) => ( - ))} @@ -582,6 +583,7 @@ export function InvoicesPage() { ))}
+ {h}
+
)} {showCreate && ( @@ -647,15 +649,15 @@ function Field({ label, children }: { label: string; children: React.ReactNode } } const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", border: "1px solid #d1d5db", - borderRadius: 4, background: "#f9fafb", cursor: "pointer", fontSize: 13, + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", + borderRadius: 6, background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 500, }; const inputStyle: React.CSSProperties = { - width: "100%", padding: "0.4rem 0.5rem", border: "1px solid #d1d5db", - borderRadius: 4, fontSize: 14, boxSizing: "border-box", + width: "100%", padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", + borderRadius: 6, fontSize: 14, boxSizing: "border-box", }; const tdStyle: React.CSSProperties = { - padding: "0.5rem 0.75rem", borderBottom: "1px solid #e2e8f0", + padding: "0.55rem 0.75rem", borderBottom: "1px solid #f3f4f6", }; diff --git a/apps/web/src/pages/Reports.tsx b/apps/web/src/pages/Reports.tsx index fabb159..f7b3ceb 100644 --- a/apps/web/src/pages/Reports.tsx +++ b/apps/web/src/pages/Reports.tsx @@ -84,14 +84,15 @@ function StatCard({ label, value, sub }: { label: string; value: string; sub?: s
-
+
{label}
{value}
@@ -102,7 +103,7 @@ function StatCard({ label, value, sub }: { label: string; value: string; sub?: s function SectionHeader({ children }: { children: React.ReactNode }) { return ( -

+

{children}

); @@ -110,35 +111,37 @@ function SectionHeader({ children }: { children: React.ReactNode }) { function Table({ headers, rows }: { headers: string[]; rows: (string | number)[][] }) { return ( - - - - {headers.map((h) => ( - - ))} - - - - {rows.map((row, i) => ( - - {row.map((cell, j) => ( - +
+
- {h} -
- {cell} -
+ + + {headers.map((h) => ( + ))} - ))} - {rows.length === 0 && ( - - - - )} - -
+ {h} +
- No data for this period. -
+ + + {rows.map((row, i) => ( + + {row.map((cell, j) => ( + + {cell} + + ))} + + ))} + {rows.length === 0 && ( + + + No data for this period. + + + )} + + +
); } @@ -267,7 +270,7 @@ export function ReportsPage() { -
@@ -390,19 +393,19 @@ export function ReportsPage() { // ─── Shared styles ──────────────────────────────────────────────────────────── const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", - borderRadius: 4, - background: "#f9fafb", + borderRadius: 6, + background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 500, }; const inputStyle: React.CSSProperties = { - padding: "0.3rem 0.4rem", + padding: "0.35rem 0.5rem", border: "1px solid #d1d5db", - borderRadius: 4, + borderRadius: 6, fontSize: 13, marginLeft: "0.25rem", }; diff --git a/apps/web/src/pages/Services.tsx b/apps/web/src/pages/Services.tsx index 229b0d7..eb952b1 100644 --- a/apps/web/src/pages/Services.tsx +++ b/apps/web/src/pages/Services.tsx @@ -119,7 +119,7 @@ export function ServicesPage() {

Services

@@ -128,11 +128,12 @@ export function ServicesPage() { {services.length === 0 ? (

No services configured yet.

) : ( +
{["Name", "Description", "Price", "Duration", "Status", ""].map((h) => ( - ))} @@ -171,6 +172,7 @@ export function ServicesPage() { ))}
+ {h}
+
)} {showForm && ( @@ -230,7 +232,7 @@ export function ServicesPage() { @@ -277,15 +279,15 @@ function Field({ label, children }: { label: string; children: React.ReactNode } } const btnStyle: React.CSSProperties = { - padding: "0.35rem 0.75rem", border: "1px solid #d1d5db", - borderRadius: 4, background: "#f9fafb", cursor: "pointer", fontSize: 13, + padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", + borderRadius: 6, background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 500, }; const inputStyle: React.CSSProperties = { - width: "100%", padding: "0.4rem 0.5rem", border: "1px solid #d1d5db", - borderRadius: 4, fontSize: 14, boxSizing: "border-box", + width: "100%", padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", + borderRadius: 6, fontSize: 14, boxSizing: "border-box", }; const tdStyle: React.CSSProperties = { - padding: "0.5rem 0.75rem", borderBottom: "1px solid #e2e8f0", + padding: "0.55rem 0.75rem", borderBottom: "1px solid #f3f4f6", }; diff --git a/apps/web/src/pages/Staff.tsx b/apps/web/src/pages/Staff.tsx index 0f34ffb..5e9b594 100644 --- a/apps/web/src/pages/Staff.tsx +++ b/apps/web/src/pages/Staff.tsx @@ -78,7 +78,7 @@ export function StaffPage() {

Staff

-
@@ -86,11 +86,12 @@ export function StaffPage() { {staff.length === 0 ? (

No staff members yet.

) : ( +
{["Name", "Email", "Role", "Status", ""].map((h) => ( - + ))} @@ -113,6 +114,7 @@ export function StaffPage() { ))}
{h}{h}
+
)} {showForm && ( @@ -143,7 +145,7 @@ export function StaffPage() {
{formError &&

{formError}

}
- @@ -156,7 +158,7 @@ export function StaffPage() { ); } -const btnStyle: React.CSSProperties = { padding: "0.35rem 0.75rem", border: "1px solid #d1d5db", borderRadius: 4, background: "#f9fafb", cursor: "pointer", fontSize: 13 }; -const inputStyle: React.CSSProperties = { width: "100%", padding: "0.4rem 0.5rem", border: "1px solid #d1d5db", borderRadius: 4, fontSize: 14, boxSizing: "border-box" }; +const btnStyle: React.CSSProperties = { padding: "0.4rem 0.85rem", border: "1px solid #d1d5db", borderRadius: 6, background: "#fff", cursor: "pointer", fontSize: 13, fontWeight: 500 }; +const inputStyle: React.CSSProperties = { width: "100%", padding: "0.45rem 0.6rem", border: "1px solid #d1d5db", borderRadius: 6, fontSize: 14, boxSizing: "border-box" }; const labelStyle: React.CSSProperties = { display: "block", fontWeight: 600, marginBottom: "0.25rem", fontSize: 13, color: "#374151" }; -const tdStyle: React.CSSProperties = { padding: "0.5rem 0.75rem", borderBottom: "1px solid #e2e8f0" }; +const tdStyle: React.CSSProperties = { padding: "0.55rem 0.75rem", borderBottom: "1px solid #f3f4f6" };