mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-09 07:03:25 +00:00
refactor(mantine): migrate home page
This commit is contained in:
16
inertia/components/common/mantine_theme_switcher.tsx
Normal file
16
inertia/components/common/mantine_theme_switcher.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useMantineColorScheme, ActionIcon } from '@mantine/core';
|
||||||
|
import { TbSun, TbMoonStars } from 'react-icons/tb';
|
||||||
|
|
||||||
|
export function MantineThemeSwitcher() {
|
||||||
|
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||||
|
return (
|
||||||
|
<ActionIcon
|
||||||
|
variant="default"
|
||||||
|
aria-label="Toggle color scheme"
|
||||||
|
onClick={() => toggleColorScheme()}
|
||||||
|
size="lg"
|
||||||
|
>
|
||||||
|
{colorScheme === 'dark' ? <TbSun /> : <TbMoonStars />}
|
||||||
|
</ActionIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { IconType } from 'react-icons/lib';
|
|
||||||
|
|
||||||
const AboutItemStyle = styled.li(({ theme }) => ({
|
|
||||||
width: '350px',
|
|
||||||
display: 'flex',
|
|
||||||
gap: '1em',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexDirection: 'column',
|
|
||||||
|
|
||||||
'& svg': {
|
|
||||||
color: theme.colors.blue,
|
|
||||||
},
|
|
||||||
|
|
||||||
'& div': {
|
|
||||||
fontSize: '1.25rem',
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
|
|
||||||
'& p': {
|
|
||||||
height: '65px',
|
|
||||||
color: theme.colors.grey,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const AboutItem = ({
|
|
||||||
title,
|
|
||||||
text,
|
|
||||||
icon: Icon,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
icon: IconType;
|
|
||||||
}) => (
|
|
||||||
<AboutItemStyle>
|
|
||||||
<Icon size={60} />
|
|
||||||
<div>{title}</div>
|
|
||||||
<p>{text}</p>
|
|
||||||
</AboutItemStyle>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default AboutItem;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import UnstyledList from '~/components/common/unstyled/unstyled_list';
|
|
||||||
|
|
||||||
const AboutList = styled(UnstyledList)({
|
|
||||||
margin: '4em 0 !important',
|
|
||||||
display: 'flex',
|
|
||||||
gap: '2em',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AboutList;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { Link } from '@inertiajs/react';
|
|
||||||
import { route } from '@izzyjs/route/client';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import Quotes from '~/components/quotes';
|
|
||||||
|
|
||||||
const HeroStyle = styled.header(({ theme }) => ({
|
|
||||||
height: '250px',
|
|
||||||
minHeight: '250px',
|
|
||||||
width: '100%',
|
|
||||||
backgroundColor: theme.colors.secondary,
|
|
||||||
marginTop: '0.5em',
|
|
||||||
borderRadius: theme.border.radius,
|
|
||||||
padding: '1em',
|
|
||||||
display: 'flex',
|
|
||||||
gap: '1em',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexDirection: 'column',
|
|
||||||
|
|
||||||
'& *': {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const HeroTitle = styled.h1({
|
|
||||||
fontSize: '32px',
|
|
||||||
});
|
|
||||||
|
|
||||||
const HeroQuote = styled(Quotes)({
|
|
||||||
fontSize: '20px',
|
|
||||||
});
|
|
||||||
|
|
||||||
const LinkButton = styled(Link)(({ theme }) => ({
|
|
||||||
fontSize: '1rem',
|
|
||||||
width: 'fit-content',
|
|
||||||
color: theme.colors.white,
|
|
||||||
backgroundColor: theme.colors.primary,
|
|
||||||
borderRadius: '5rem',
|
|
||||||
padding: '0.5em 1.5em',
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function HeroHeader() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<HeroStyle>
|
|
||||||
<HeroTitle>{t('about:hero.title')}</HeroTitle>
|
|
||||||
<HeroQuote>{t('common:slogan')}</HeroQuote>
|
|
||||||
<LinkButton href={route('dashboard').url}>
|
|
||||||
{t('about:hero.cta')}
|
|
||||||
</LinkButton>
|
|
||||||
</HeroStyle>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const ScreenshotWrapper = styled.div({
|
|
||||||
position: 'relative',
|
|
||||||
height: '360px',
|
|
||||||
width: '640px',
|
|
||||||
maxWidth: '100%',
|
|
||||||
margin: '0 auto',
|
|
||||||
});
|
|
||||||
|
|
||||||
const Screenshot = styled.img(({ theme }) => ({
|
|
||||||
height: 'auto !important',
|
|
||||||
width: '100%',
|
|
||||||
boxShadow: theme.colors.boxShadow,
|
|
||||||
borderRadius: theme.border.radius,
|
|
||||||
overflow: 'hidden',
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default function WebsitePreview() {
|
|
||||||
const { t } = useTranslation('about');
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2>{t('look-title')}</h2>
|
|
||||||
<ScreenshotWrapper>
|
|
||||||
<Screenshot
|
|
||||||
src="/website-screenshot.png"
|
|
||||||
alt={t('website-screenshot-alt')}
|
|
||||||
title={t('website-screenshot-alt')}
|
|
||||||
/>
|
|
||||||
</ScreenshotWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import '@mantine/core/styles.css';
|
import '@mantine/core/styles.css';
|
||||||
import '@mantine/spotlight/styles.css';
|
import '@mantine/spotlight/styles.css';
|
||||||
|
import '../../../styles/index.css';
|
||||||
|
|
||||||
const theme = createTheme({});
|
const theme = createTheme({});
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,23 @@ import MantineNavbar from '~/components/navbar/mantine_navbar';
|
|||||||
|
|
||||||
function MantineContentLayout({ children }: PropsWithChildren) {
|
function MantineContentLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<div
|
<Container
|
||||||
style={{
|
style={{
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
width: '100%',
|
display: 'flex',
|
||||||
backgroundColor: 'rgb(34, 40, 49)',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container>
|
<MantineNavbar />
|
||||||
<MantineNavbar />
|
<main
|
||||||
<main>{children}</main>
|
style={{
|
||||||
<MantineFooter />
|
flex: 1,
|
||||||
</Container>
|
}}
|
||||||
</div>
|
>
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
<MantineFooter />
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ExternalLink from '~/components/common/external_link';
|
import ExternalLink from '~/components/common/external_link';
|
||||||
|
import { MantineThemeSwitcher } from '~/components/common/mantine_theme_switcher';
|
||||||
import classes from './mobile_navbar.module.css';
|
import classes from './mobile_navbar.module.css';
|
||||||
|
|
||||||
export default function MantineNavbar() {
|
export default function MantineNavbar() {
|
||||||
@@ -29,7 +30,7 @@ export default function MantineNavbar() {
|
|||||||
<Image src="/logo-light.png" h={40} alt="MyLinks's logo" />
|
<Image src="/logo-light.png" h={40} alt="MyLinks's logo" />
|
||||||
|
|
||||||
<Group h="100%" gap={0} visibleFrom="sm">
|
<Group h="100%" gap={0} visibleFrom="sm">
|
||||||
<Link href="#" className={classes.link}>
|
<Link href="/" className={classes.link}>
|
||||||
{t('home')}
|
{t('home')}
|
||||||
</Link>
|
</Link>
|
||||||
<ExternalLink href={PATHS.REPO_GITHUB} className={classes.link}>
|
<ExternalLink href={PATHS.REPO_GITHUB} className={classes.link}>
|
||||||
@@ -40,17 +41,21 @@ export default function MantineNavbar() {
|
|||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Group visibleFrom="sm">
|
<Group>
|
||||||
<Button component={Link} href={route('auth.login').url}>
|
<MantineThemeSwitcher />
|
||||||
|
<Button
|
||||||
|
component={Link}
|
||||||
|
href={route('auth.login').url}
|
||||||
|
visibleFrom="sm"
|
||||||
|
>
|
||||||
{t('login')}
|
{t('login')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Burger
|
||||||
|
opened={drawerOpened}
|
||||||
|
onClick={toggleDrawer}
|
||||||
|
hiddenFrom="sm"
|
||||||
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Burger
|
|
||||||
opened={drawerOpened}
|
|
||||||
onClick={toggleDrawer}
|
|
||||||
hiddenFrom="sm"
|
|
||||||
/>
|
|
||||||
</Group>
|
</Group>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"title": "Welcome to MyLinks",
|
"title": "Welcome to MyLinks",
|
||||||
"cta": "Get started"
|
"cta": "Get started"
|
||||||
},
|
},
|
||||||
|
"description": "An open-source, self-hosted bookmark manager that lets you manage your favorite links in an intuitive interface",
|
||||||
"collection": {
|
"collection": {
|
||||||
"title": "Create collections",
|
"title": "Create collections",
|
||||||
"text": "Organize your bookmarks by collections to keep your links tidy and find them easily."
|
"text": "Organize your bookmarks by collections to keep your links tidy and find them easily."
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"title": "Bienvenue sur MyLinks",
|
"title": "Bienvenue sur MyLinks",
|
||||||
"cta": "Lancez-vous !"
|
"cta": "Lancez-vous !"
|
||||||
},
|
},
|
||||||
|
"description": "Un gestionnaire de favoris open-source et auto-hébergé qui vous permet de gérer vos liens favoris dans une interface intuitive",
|
||||||
"collection": {
|
"collection": {
|
||||||
"title": "Créer des collections",
|
"title": "Créer des collections",
|
||||||
"text": "Organisez vos favoris dans des collections pour garder vos liens en ordre et les retrouver facilement."
|
"text": "Organisez vos favoris dans des collections pour garder vos liens en ordre et les retrouver facilement."
|
||||||
|
|||||||
26
inertia/pages/home.module.css
Normal file
26
inertia/pages/home.module.css
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
.wrapper {
|
||||||
|
padding-top: calc(var(--mantine-spacing-xl) * 2);
|
||||||
|
padding-bottom: calc(var(--mantine-spacing-xl) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family:
|
||||||
|
Greycliff CF,
|
||||||
|
var(--mantine-font-family);
|
||||||
|
font-weight: 900;
|
||||||
|
margin-bottom: var(--mantine-spacing-md);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (max-width: $mantine-breakpoint-sm) {
|
||||||
|
font-size: rem(28px);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (max-width: $mantine-breakpoint-sm) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,68 +1,95 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import { MantineContentLayout } from '~/components/layouts/mantine/mantine_content_layout';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
SimpleGrid,
|
||||||
|
Text,
|
||||||
|
ThemeIcon,
|
||||||
|
Title,
|
||||||
|
rem,
|
||||||
|
} from '@mantine/core';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AiFillFolderOpen } from 'react-icons/ai';
|
import { AiFillFolderOpen } from 'react-icons/ai';
|
||||||
import { FaUser } from 'react-icons/fa';
|
import { FaUser } from 'react-icons/fa';
|
||||||
import { IoIosLink, IoIosSearch, IoIosShareAlt } from 'react-icons/io';
|
import { IoIosLink, IoIosSearch, IoIosShareAlt } from 'react-icons/io';
|
||||||
import { IoExtensionPuzzleOutline } from 'react-icons/io5';
|
import { IoExtensionPuzzleOutline } from 'react-icons/io5';
|
||||||
import AboutItem from '~/components/home/about/about_item';
|
import classes from './home.module.css';
|
||||||
import AboutList from '~/components/home/about/about_list';
|
|
||||||
import HeroHeader from '~/components/home/hero_header';
|
|
||||||
import WebsitePreview from '~/components/home/website_preview';
|
|
||||||
import ContentLayout from '~/components/layouts/content_layout';
|
|
||||||
|
|
||||||
const PageContent = styled.div({
|
const features = [
|
||||||
marginBottom: '4em',
|
'collection',
|
||||||
textAlign: 'center',
|
'link',
|
||||||
display: 'flex',
|
'search',
|
||||||
gap: '2em',
|
'extension',
|
||||||
flex: 1,
|
'share',
|
||||||
flexDirection: 'column',
|
'contribute',
|
||||||
});
|
] as const;
|
||||||
|
type FeatureName = (typeof features)[number];
|
||||||
|
|
||||||
function HomePage() {
|
interface FeatureProps {
|
||||||
const { t } = useTranslation();
|
name: FeatureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon(name: FeatureName) {
|
||||||
|
switch (name) {
|
||||||
|
case 'collection':
|
||||||
|
return AiFillFolderOpen;
|
||||||
|
case 'link':
|
||||||
|
return IoIosLink;
|
||||||
|
case 'search':
|
||||||
|
return IoIosSearch;
|
||||||
|
case 'extension':
|
||||||
|
return IoExtensionPuzzleOutline;
|
||||||
|
case 'share':
|
||||||
|
return IoIosShareAlt;
|
||||||
|
case 'contribute':
|
||||||
|
return FaUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Feature({ name: featureName }: FeatureProps) {
|
||||||
|
const { t } = useTranslation('about');
|
||||||
|
const Icon = getIcon(featureName);
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<HeroHeader />
|
<ThemeIcon variant="light" size={40} radius={40}>
|
||||||
<PageContent>
|
<Icon style={{ width: rem(18), height: rem(18) }} />
|
||||||
<AboutList>
|
</ThemeIcon>
|
||||||
<AboutItem
|
<Text mt="sm" mb={7}>
|
||||||
icon={AiFillFolderOpen}
|
{t(`${featureName}.title`)}
|
||||||
title={t('about:collection.title')}
|
</Text>
|
||||||
text={t('about:collection.text')}
|
<Text size="sm" c="dimmed" lh={1.6}>
|
||||||
/>
|
{t(`${featureName}.text`)}
|
||||||
<AboutItem
|
</Text>
|
||||||
icon={IoIosLink}
|
</div>
|
||||||
title={t('about:link.title')}
|
|
||||||
text={t('about:link.text')}
|
|
||||||
/>
|
|
||||||
<AboutItem
|
|
||||||
icon={IoIosSearch}
|
|
||||||
title={t('about:search.title')}
|
|
||||||
text={t('about:search.text')}
|
|
||||||
/>
|
|
||||||
<AboutItem
|
|
||||||
icon={IoExtensionPuzzleOutline}
|
|
||||||
title={t('about:extension.title')}
|
|
||||||
text={t('about:extension.text')}
|
|
||||||
/>
|
|
||||||
<AboutItem
|
|
||||||
icon={IoIosShareAlt}
|
|
||||||
title={t('about:share.title')}
|
|
||||||
text={t('about:share.text')}
|
|
||||||
/>
|
|
||||||
<AboutItem
|
|
||||||
icon={FaUser}
|
|
||||||
title={t('about:contribute.title')}
|
|
||||||
text={t('about:contribute.text')}
|
|
||||||
/>
|
|
||||||
</AboutList>
|
|
||||||
<WebsitePreview />
|
|
||||||
</PageContent>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HomePage.layout = (page: ReactNode) => <ContentLayout children={page} />;
|
function HomePage() {
|
||||||
|
const { t } = useTranslation('about');
|
||||||
|
return (
|
||||||
|
<Container className={classes.wrapper}>
|
||||||
|
<Title className={classes.title}>{t('hero.title')}</Title>
|
||||||
|
|
||||||
|
<Container size={560} p={0}>
|
||||||
|
<Text size="sm" className={classes.description}>
|
||||||
|
{t('description')}
|
||||||
|
</Text>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<SimpleGrid
|
||||||
|
mt={60}
|
||||||
|
cols={{ base: 1, sm: 2, md: 3 }}
|
||||||
|
spacing={{ base: 'xl', md: 50 }}
|
||||||
|
verticalSpacing={{ base: 'xl', md: 50 }}
|
||||||
|
>
|
||||||
|
{features.map((feature, index) => (
|
||||||
|
<Feature name={feature} key={index} />
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
HomePage.layout = (page: ReactNode) => <MantineContentLayout children={page} />;
|
||||||
export default HomePage;
|
export default HomePage;
|
||||||
|
|||||||
6
inertia/styles/index.css
Normal file
6
inertia/styles/index.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100svh;
|
||||||
|
width: 100%;
|
||||||
|
background-color: light-dark(var(--mantine-color-white), rgb(34, 40, 49));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user