Skip to content

Commit

Permalink
feat: landing!
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher2K committed Nov 28, 2024
1 parent bf65762 commit 4e46c69
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 49 deletions.
11 changes: 11 additions & 0 deletions swapify_web/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
/// <reference path="../.astro/types.d.ts" />

interface ImportMetaEnv {
readonly PUBLIC_API_URL: string;
readonly PUBLIC_APP_URL: string;
readonly PUBLIC_HIGHLIGHT_PROJECT_ID: string;
readonly PUBLIC_APP_VERSION: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}
258 changes: 258 additions & 0 deletions swapify_web/src/features/marketing/landing-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import {
SparklesIcon,
ArrowRightIcon,
Music2Icon,
RefreshCwIcon,
SwatchBookIcon,
} from "lucide-react";

import { Text } from "#root/components/ui/text";
import { VStack, HStack, Box, type BoxProps, Stack } from "#style/jsx";
import { css } from "#style/css";
import { Heading } from "#root/components/ui/heading";
import type { PropsWithChildren } from "react";
import { Button } from "#root/components/ui/button";
import { Card } from "#root/components/ui/card";
import type { ReactNode } from "@tanstack/react-router";

const ContentContainer = ({
children,
...props
}: PropsWithChildren & BoxProps) => (
<Box w="full" mx="auto" {...props}>
<Box maxWidth="1400px" w="full" mx="auto" px="10">
{children}
</Box>
</Box>
);

const BetaBanner = () => (
<Box
backgroundColor="neutral.12"
width="full"
justifyContent="center"
alignItems="center"
gap="2"
px="2"
py="2"
>
<Text color="neutral.1" size="sm" fontWeight="medium" textAlign="center">
<SparklesIcon
className={css({
stroke: "neutral.1",
display: "inline",
marginRight: "2",
})}
size={16}
/>
Currently in beta - Early access available now!
</Text>
</Box>
);

const Hero = () => (
<ContentContainer bg="white">
<VStack
w="full"
gap="4"
justifyContent="center"
alignItems="center"
py={["12", undefined, "24", "32", "48"]}
>
<Heading
as="h1"
size={["5xl", null, null, "7xl"]}
textAlign="center"
textWrap="balance"
>
Your Music, Your Choice
</Heading>
<Heading
as="h2"
size="xl"
textAlign="center"
textWrap="balance"
fontWeight="medium"
color="neutral.11"
mb="10"
>
Seamlessly transfer your music library between Spotify and Apple Music.
<br />
Take control of your musical journey.
</Heading>

<Button size="2xl" asChild>
<a href="/app">
Start transfer now
<ArrowRightIcon className={css({})} />
</a>
</Button>
</VStack>
</ContentContainer>
);

type MarketingCardProps = {
icon: ReactNode;
title: string;
text: string;
};
const MarketingCard = ({ icon, title, text }: MarketingCardProps) => {
return (
<Card.Root>
<Card.Header justifyContent="center" alignItems="center" gap="4">
{icon}
<Heading as="h3" size="xl" textAlign="center">
{title}
</Heading>
</Card.Header>
<Card.Body>
<Text textAlign="center" color="neutral.11">
{text}
</Text>
</Card.Body>
</Card.Root>
);
};

const MarketingCards = () => (
<ContentContainer bg="bg.muted">
<Stack
gap={["10"]}
direction={["column", null, null, "row"]}
py={["12", undefined, "24", "32"]}
>
<MarketingCard
icon={<Music2Icon size={28} />}
title="Musical mobility"
text="Take your carefully curated playlists and library wherever you go. Switch between services without losing your musical identity."
/>

<MarketingCard
icon={<RefreshCwIcon size={28} />}
title="Freedom to Switch"
text="Compare prices, features, and music availability. Choose the service that best fits your needs without compromise."
/>

<MarketingCard
icon={<SwatchBookIcon size={28} />}
title="More to Come"
text="We're just getting started! More streaming services, smart playlist management, and advanced features are on the horizon."
/>
</Stack>
</ContentContainer>
);

type ComingSoonItemProps = {
title: string;
text: string;
};
const ComingSoonItem = ({ title, text }: ComingSoonItemProps) => (
<HStack
border="0.5"
px="4"
py="4"
borderRadius="md"
borderStyle="solid"
borderColor="neutral.4"
flex="1"
flexBasis="0"
justifyContent={["flex-start", null, null, "center"]}
alignItems="center"
gap={["4", null, null, "2"]}
>
<SparklesIcon size={24} className={css({ flexShrink: "0" })} />
<VStack gap="1" flex="1">
<Heading
as="h3"
size="xl"
textAlign={["left", null, null, "center"]}
w="full"
>
{title}
</Heading>
<Text
color="neutral.11"
textAlign={["left", null, null, "center"]}
w="full"
>
{text}
</Text>
</VStack>
</HStack>
);

const ComingSoon = () => (
<ContentContainer bg="white">
<VStack w="full" py={["12", undefined, "24", "32"]} gap="12">
<Box width="full">
<Heading as="h2" size="3xl" textAlign="center" mb="4">
Coming soon
</Heading>
<Text textAlign="center" size="xl" color="neutral.11">
We're working hard to bring you even more amazing features
</Text>
</Box>

<Stack gap={["10"]} direction={["column", null, null, "row"]}>
<ComingSoonItem
title="More services"
text="Tidal, Deezer, Qobuz, Youtube Music and more coming soon!"
/>

<ComingSoonItem
title="Playlist manageement"
text="Sync & share playlist accross different platforms"
/>

<ComingSoonItem
title="Playlist curator profile"
text="Automatic musical management for playlist curators"
/>
</Stack>
</VStack>
</ContentContainer>
);

const CallToAction = () => (
<ContentContainer bg="bg.muted">
<VStack w="full" py={["12", undefined, "24", "32"]} gap="12">
<Box width="full">
<Heading as="h2" size="3xl" textAlign="center" mb="4">
Ready to break free?
</Heading>
<Text textAlign="center" size="xl" color="neutral.11">
Join our beta program today and experience the future of music library
management.
</Text>
</Box>

<Button size="xl" asChild>
<a href="/app">
Get Started Now
<ArrowRightIcon className={css({})} />
</a>
</Button>
</VStack>
</ContentContainer>
);

const Footer = () => (
<ContentContainer bg="white">
<VStack w="full" py="12">
<Text>© 2024 Swapify. All rights reserved.</Text>
</VStack>
</ContentContainer>
);

export const LandingPage = () => {
return (
<Box>
<BetaBanner />
<Hero />
<MarketingCards />
<ComingSoon />
<CallToAction />
<Footer />
</Box>
);
};
48 changes: 13 additions & 35 deletions swapify_web/src/layouts/AppLayout.astro
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Swapify</title>
</head>
---
import RootLayout from "./RootLayout.astro";
---

<body>
<main id="root">
<slot />
</main>
<RootLayout title="Swapify">
<slot />
<slot name="scripts">
<script src="https://js-cdn.music.apple.com/musickit/v3/musickit.js"
></script>
<script>
import "../../styled-system/styles.css";
import { H } from "highlight.run";
</slot>
</RootLayout>

if (window.MusicKit) {
console.debug("[Debug] MusicKit loaded");
}

H.init(import.meta.env.PUBLIC_HIGHLIGHT_PROJECT_ID, {
serviceName: "SwapifyWeb",
version: import.meta.env.PUBLIC_APP_VERSION,
debug: import.meta.env.DEV,
reportConsoleErrors: true,
manualStart: import.meta.env.DEV,
environment: import.meta.env.DEV ? "local" : "production",
tracingOrigins: true,
networkRecording: {
enabled: true,
disableWebSocketEventRecordings: true,
urlBlocklist: [],
},
});
</script>
</body>
</html>
<script is:inline>
if (window.MusicKit) {
console.debug("[Debug] MusicKit loaded");
}
</script>
Loading

0 comments on commit 4e46c69

Please sign in to comment.