refactor(frontend): consolidate shared styles into centralized style system
- Create comprehensive shared.ts with design tokens and categorized styles: - layoutStyles: main, loader, content variants - cardStyles: card, tableCard, cardHeader - tableStyles: complete table styling - paginationStyles: pagination controls - formStyles: inputs, labels, errors - buttonStyles: primary, secondary, accent, danger variants - badgeStyles: status badges with color variants - bannerStyles: error/success banners - modalStyles: modal overlay and content - toastStyles: toast notifications - utilityStyles: divider, emptyState, etc. - Refactor all page components to use shared styles: - page.tsx (counter) - audit/page.tsx - booking/page.tsx - appointments/page.tsx - profile/page.tsx - invites/page.tsx - admin/invites/page.tsx - admin/availability/page.tsx - Reduce code duplication significantly (each page now has only truly page-specific styles) - Maintain backwards compatibility with sharedStyles export
This commit is contained in:
parent
4d9edd7fd4
commit
81cd34b0e7
9 changed files with 1148 additions and 1173 deletions
|
|
@ -9,145 +9,16 @@ import { useRequireAuth } from "../hooks/useRequireAuth";
|
|||
import { components } from "../generated/api";
|
||||
import { formatDateTime } from "../utils/date";
|
||||
import { getStatusDisplay } from "../utils/appointment";
|
||||
import { sharedStyles } from "../styles/shared";
|
||||
import {
|
||||
layoutStyles,
|
||||
typographyStyles,
|
||||
bannerStyles,
|
||||
badgeStyles,
|
||||
buttonStyles,
|
||||
} from "../styles/shared";
|
||||
|
||||
type AppointmentResponse = components["schemas"]["AppointmentResponse"];
|
||||
|
||||
const pageStyles: Record<string, React.CSSProperties> = {
|
||||
main: {
|
||||
minHeight: "100vh",
|
||||
background: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #2d1b4e 100%)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
loader: {
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
padding: "2rem",
|
||||
maxWidth: "800px",
|
||||
margin: "0 auto",
|
||||
width: "100%",
|
||||
},
|
||||
pageTitle: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1.75rem",
|
||||
fontWeight: 600,
|
||||
color: "#fff",
|
||||
marginBottom: "0.5rem",
|
||||
},
|
||||
pageSubtitle: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
fontSize: "0.9rem",
|
||||
marginBottom: "1.5rem",
|
||||
},
|
||||
section: {
|
||||
marginBottom: "2rem",
|
||||
},
|
||||
sectionTitle: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1.1rem",
|
||||
fontWeight: 500,
|
||||
color: "#fff",
|
||||
marginBottom: "1rem",
|
||||
},
|
||||
sectionTitleMuted: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1.1rem",
|
||||
fontWeight: 500,
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
marginBottom: "1rem",
|
||||
},
|
||||
appointmentList: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "0.75rem",
|
||||
},
|
||||
appointmentCard: {
|
||||
background: "rgba(255, 255, 255, 0.03)",
|
||||
border: "1px solid rgba(255, 255, 255, 0.08)",
|
||||
borderRadius: "12px",
|
||||
padding: "1.25rem",
|
||||
transition: "all 0.2s",
|
||||
},
|
||||
appointmentCardPast: {
|
||||
opacity: 0.6,
|
||||
background: "rgba(255, 255, 255, 0.01)",
|
||||
},
|
||||
appointmentHeader: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
gap: "1rem",
|
||||
},
|
||||
appointmentTime: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1rem",
|
||||
fontWeight: 500,
|
||||
color: "#fff",
|
||||
marginBottom: "0.25rem",
|
||||
},
|
||||
appointmentNote: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "0.875rem",
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
marginBottom: "0.5rem",
|
||||
},
|
||||
statusBadge: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: 500,
|
||||
padding: "0.25rem 0.75rem",
|
||||
borderRadius: "9999px",
|
||||
display: "inline-block",
|
||||
},
|
||||
buttonGroup: {
|
||||
display: "flex",
|
||||
gap: "0.5rem",
|
||||
},
|
||||
cancelButton: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
padding: "0.35rem 0.75rem",
|
||||
fontSize: "0.75rem",
|
||||
background: "rgba(255, 255, 255, 0.05)",
|
||||
border: "1px solid rgba(255, 255, 255, 0.1)",
|
||||
borderRadius: "6px",
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s",
|
||||
},
|
||||
confirmButton: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
padding: "0.35rem 0.75rem",
|
||||
fontSize: "0.75rem",
|
||||
background: "rgba(239, 68, 68, 0.2)",
|
||||
border: "1px solid rgba(239, 68, 68, 0.3)",
|
||||
borderRadius: "6px",
|
||||
color: "#f87171",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s",
|
||||
},
|
||||
emptyState: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
color: "rgba(255, 255, 255, 0.4)",
|
||||
textAlign: "center",
|
||||
padding: "3rem",
|
||||
},
|
||||
emptyStateLink: {
|
||||
color: "#a78bfa",
|
||||
textDecoration: "none",
|
||||
},
|
||||
};
|
||||
|
||||
const styles = { ...sharedStyles, ...pageStyles };
|
||||
|
||||
export default function AppointmentsPage() {
|
||||
const { user, isLoading, isAuthorized } = useRequireAuth({
|
||||
requiredPermission: Permission.VIEW_OWN_APPOINTMENTS,
|
||||
|
|
@ -195,8 +66,8 @@ export default function AppointmentsPage() {
|
|||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<main style={styles.main}>
|
||||
<div style={styles.loader}>Loading...</div>
|
||||
<main style={layoutStyles.main}>
|
||||
<div style={layoutStyles.loader}>Loading...</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
@ -213,13 +84,13 @@ export default function AppointmentsPage() {
|
|||
);
|
||||
|
||||
return (
|
||||
<main style={styles.main}>
|
||||
<main style={layoutStyles.main}>
|
||||
<Header currentPage="appointments" />
|
||||
<div style={styles.content}>
|
||||
<h1 style={styles.pageTitle}>My Appointments</h1>
|
||||
<p style={styles.pageSubtitle}>View and manage your booked appointments</p>
|
||||
<h1 style={typographyStyles.pageTitle}>My Appointments</h1>
|
||||
<p style={typographyStyles.pageSubtitle}>View and manage your booked appointments</p>
|
||||
|
||||
{error && <div style={styles.errorBanner}>{error}</div>}
|
||||
{error && <div style={bannerStyles.errorBanner}>{error}</div>}
|
||||
|
||||
{isLoadingAppointments ? (
|
||||
<div style={styles.emptyState}>Loading appointments...</div>
|
||||
|
|
@ -249,7 +120,7 @@ export default function AppointmentsPage() {
|
|||
{apt.note && <div style={styles.appointmentNote}>{apt.note}</div>}
|
||||
<span
|
||||
style={{
|
||||
...styles.statusBadge,
|
||||
...badgeStyles.badge,
|
||||
background: status.bgColor,
|
||||
color: status.textColor,
|
||||
}}
|
||||
|
|
@ -271,7 +142,7 @@ export default function AppointmentsPage() {
|
|||
</button>
|
||||
<button
|
||||
onClick={() => setConfirmCancelId(null)}
|
||||
style={styles.cancelButton}
|
||||
style={buttonStyles.secondaryButton}
|
||||
>
|
||||
No
|
||||
</button>
|
||||
|
|
@ -279,7 +150,7 @@ export default function AppointmentsPage() {
|
|||
) : (
|
||||
<button
|
||||
onClick={() => setConfirmCancelId(apt.id)}
|
||||
style={styles.cancelButton}
|
||||
style={buttonStyles.secondaryButton}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
|
@ -297,7 +168,7 @@ export default function AppointmentsPage() {
|
|||
{/* Past/Cancelled Appointments */}
|
||||
{pastOrCancelledAppointments.length > 0 && (
|
||||
<div style={styles.section}>
|
||||
<h2 style={styles.sectionTitleMuted}>
|
||||
<h2 style={typographyStyles.sectionTitleMuted}>
|
||||
Past & Cancelled ({pastOrCancelledAppointments.length})
|
||||
</h2>
|
||||
<div style={styles.appointmentList}>
|
||||
|
|
@ -312,7 +183,7 @@ export default function AppointmentsPage() {
|
|||
{apt.note && <div style={styles.appointmentNote}>{apt.note}</div>}
|
||||
<span
|
||||
style={{
|
||||
...styles.statusBadge,
|
||||
...badgeStyles.badge,
|
||||
background: status.bgColor,
|
||||
color: status.textColor,
|
||||
}}
|
||||
|
|
@ -331,3 +202,84 @@ export default function AppointmentsPage() {
|
|||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
// Page-specific styles
|
||||
const styles: Record<string, React.CSSProperties> = {
|
||||
content: {
|
||||
flex: 1,
|
||||
padding: "2rem",
|
||||
maxWidth: "800px",
|
||||
margin: "0 auto",
|
||||
width: "100%",
|
||||
},
|
||||
section: {
|
||||
marginBottom: "2rem",
|
||||
},
|
||||
sectionTitle: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1.1rem",
|
||||
fontWeight: 500,
|
||||
color: "#fff",
|
||||
marginBottom: "1rem",
|
||||
},
|
||||
appointmentList: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "0.75rem",
|
||||
},
|
||||
appointmentCard: {
|
||||
background: "rgba(255, 255, 255, 0.03)",
|
||||
border: "1px solid rgba(255, 255, 255, 0.08)",
|
||||
borderRadius: "12px",
|
||||
padding: "1.25rem",
|
||||
transition: "all 0.2s",
|
||||
},
|
||||
appointmentCardPast: {
|
||||
opacity: 0.6,
|
||||
background: "rgba(255, 255, 255, 0.01)",
|
||||
},
|
||||
appointmentHeader: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
gap: "1rem",
|
||||
},
|
||||
appointmentTime: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "1rem",
|
||||
fontWeight: 500,
|
||||
color: "#fff",
|
||||
marginBottom: "0.25rem",
|
||||
},
|
||||
appointmentNote: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
fontSize: "0.875rem",
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
marginBottom: "0.5rem",
|
||||
},
|
||||
buttonGroup: {
|
||||
display: "flex",
|
||||
gap: "0.5rem",
|
||||
},
|
||||
confirmButton: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
padding: "0.35rem 0.75rem",
|
||||
fontSize: "0.75rem",
|
||||
background: "rgba(239, 68, 68, 0.2)",
|
||||
border: "1px solid rgba(239, 68, 68, 0.3)",
|
||||
borderRadius: "6px",
|
||||
color: "#f87171",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s",
|
||||
},
|
||||
emptyState: {
|
||||
fontFamily: "'DM Sans', system-ui, sans-serif",
|
||||
color: "rgba(255, 255, 255, 0.4)",
|
||||
textAlign: "center",
|
||||
padding: "3rem",
|
||||
},
|
||||
emptyStateLink: {
|
||||
color: "#a78bfa",
|
||||
textDecoration: "none",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue