Skip to content
Open
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
32 changes: 0 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 2 additions & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import { Footer, Header } from "compositions";
import { AllProviders } from "data";
import { Demo } from "./examples/Demo";
import { FAQs } from "./examples/FAQs";
import { PanelSections } from "./examples/PanelSections";
import { PricingGrid } from "./examples/PricingGrid";
import { ProductDetails } from "./examples/ProductDetails";
import { ProductGrid } from "./examples/ProductGrid";
import { WelcomeHero } from "./examples/WelcomeHero";
import AppointmentBooking from "./examples/AppointmentBooking";

function App() {
return (
<AllProviders>
<Header />
<Demo />
<WelcomeHero />
<PanelSections />
<PricingGrid />
<FAQs />
<ProductDetails />
<ProductGrid />
<AppointmentBooking />
<Footer />
</AllProviders>
);
Expand Down
39 changes: 39 additions & 0 deletions src/data/services/servicesService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Service } from "../types/service";

/**
* Mock services service
*/
export const servicesService = {
/**
* Get all services
*/
async getServices(): Promise<Service[]> {
// Simulate API delay
await new Promise((resolve) => setTimeout(resolve, 1000));
return [
{
id: "1",
name: "Haircut",
price: 30.0,
description: "A stylish haircut",
durationInMinutes: 30,
},
{
id: "2",
name: "Haircut w/ Foil Razor & Beard Trim",
price: 50.0,
description: "A stylish haircut with a foil razor and beard trim",
durationInMinutes: 45,
signatureCuts: ["tapered cut", "foil razor finish"],
},
{
id: "3",
name: "Beard Trim(no haircut)",
price: 25.0,
description: "A stylish beard trim without a haircut",
durationInMinutes: 30,
imageUrl: "https://images.unsplash.com/photo-1503951914875-452162b0f3f1?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
},
];
}
};
12 changes: 12 additions & 0 deletions src/data/types/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Service types
*/
export type Service = {
id: string;
name: string;
description: string;
price: number;
durationInMinutes: number;
imageUrl?: string;
signatureCuts?: string[];
};
81 changes: 81 additions & 0 deletions src/examples/AppointmentBooking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { ServiceItemCard, ServiceItemCardSkeleton } from "compositions";
import { servicesService } from "data/services/servicesService";
import { Service } from "data/types/service";
import { useMediaQuery } from "hooks";
import { Flex, Section } from "layout";
import { TextContentHeading } from "primitives";
import type { FC } from "react";
import { useEffect, useState } from "react";
import "./appointmentbooking.css";

export const AppointmentBooking: FC = () => {
const [services, setServices] = useState<Service[] | null>(null);
const [loading, setLoading] = useState(true);
const { isMobile } = useMediaQuery();
const sectionPadding = isMobile ? "800" : "1600";

useEffect(() => {
let mounted = true;
const load = async () => {
try {
const data = await servicesService.getServices();
if (!mounted) return;
setServices(data);
} catch (err) {
console.error("Failed to load services", err);
if (mounted) setServices([]);
} finally {
if (mounted) setLoading(false);
}
};

load();
return () => {
mounted = false;
};
}, []);

return (
<Section
padding={sectionPadding}
className="appointment-booking-section"
variant="subtle"
>
<Flex direction="column" alignSecondary="stretch" gap={"1200"}>
<TextContentHeading
heading="Book an appointment"
subheading="We offer several services at Joe’s Barbershop"
className="appointment-booking-heading"
/>
{loading ? (
<>
<ServiceItemCardSkeleton />
<ServiceItemCardSkeleton />
<ServiceItemCardSkeleton />
<ServiceItemCardSkeleton />
</>
) : (
services?.map((service, i) => {
return (
<ServiceItemCard
key={"ServiceCard-" + service.id}
id={service.id}
heading={service.name}
price={service.price}
duration={service.durationInMinutes}
imageUrl={service.imageUrl}
signatureCuts={
service.signatureCuts?.length
? service.signatureCuts?.join(", ")
: ""
}
/>
);
})
)}
</Flex>
</Section>
);
};

export default AppointmentBooking;
33 changes: 33 additions & 0 deletions src/examples/appointmentbooking.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.service-item-card-asset {
width: 160px;
height: 160px;
border-radius: 0px;
object-fit: cover;
}

.appointment-booking-section{
padding: var(--sds-size-space-1600);
}

@media screen and (max-width: 768px) {
.appointment-booking-section {
padding: var(--sds-size-space-800);
}
}

.appointment-booking-heading p{
color: var(--sds-color-text-default-secondary);
}

.appointment-booking-heading h3{
color: var(--sds-color-text-default-default);
}

.book-now-button {
padding: var(--sds-size-space-300);
font-size: var(--sds-font-size-200);
border-radius: var(--sds-size-radius-200);
border: var(--sds-size-stroke-border) solid
var(--sds-color-border-neutral-secondary);
background: var(--sds-color-background-neutral-tertiary);
}
Loading