Skip to content

Commit

Permalink
Merge pull request #50 from Rikthepixel/feature/skeleton-loaders
Browse files Browse the repository at this point in the history
Feature/skeleton loaders
  • Loading branch information
Rikthepixel authored Jan 11, 2024
2 parents a362172 + 423e4c1 commit 8f9b682
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 153 deletions.
258 changes: 145 additions & 113 deletions apps/frontend/src/pages/ads/[uid]/index.page.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,145 @@
import { getImageUrl } from '@/apis/ads/images';
import PageContainer from '@/components/page/Container';
import useCurrencyFormatter from '@/hooks/useCurrencyFormatter';
import useTypedParams from '@/hooks/useTypedParams';
import useAdvertisements from '@/stores/useAdvertisements';
import { Carousel } from '@mantine/carousel';
import {
Image,
AspectRatio,
Paper,
Text,
Badge,
Stack,
Group,
SimpleGrid,
} from '@mantine/core';
import { useEffect } from 'react';
import { MdPerson } from 'react-icons/md';
import { z } from 'zod';

const PARAMS_SCHEMA = z.object({
uid: z.string().uuid(),
});

export default function AdPage() {
const { uid } = useTypedParams(PARAMS_SCHEMA) ?? {};
const { advertisement, getAdvertisement } = useAdvertisements();
const currencyFormatter = useCurrencyFormatter(
advertisement.unwrapValue()?.currency ?? 'EUR',
);

useEffect(() => {
if (!uid) return;
getAdvertisement(uid);
}, [getAdvertisement, uid]);

return (
<PageContainer maw="1000px">
{advertisement
.map((advertisement) => (
<Stack gap="md">
<Carousel
w="100%"
slideSize={{ base: '100%', sm: '50%' }}
slideGap={{ base: 0, sm: 'md' }}
align="start"
withControls
withIndicators
>
{advertisement.images.map((img, idx) => (
<Carousel.Slide key={idx}>
<Paper h="100%" pos="relative" withBorder>
<AspectRatio ratio={4 / 3}>
<Image
src={getImageUrl(img)}
radius="lg"
fallbackSrc="/placeholder-ad-img.webp"
/>
</AspectRatio>
</Paper>
</Carousel.Slide>
))}
</Carousel>
<Stack gap="sm">
<Group>
<Badge size="xl">
{currencyFormatter.format(advertisement.price)}
</Badge>
<Text component="h1" fz="2rem" fw={600}>
{advertisement.title}
</Text>
</Group>
<Group gap="sm" fz="md">
<MdPerson />
<Text>{advertisement.user.name}</Text>
</Group>
</Stack>
<Stack gap="sm">
<Text fz="sm" fw={700}>
Description
</Text>
<Text style={{ whiteSpace: 'pre' }}>
{advertisement.description}
</Text>
</Stack>
<Stack gap="sm">
<Text fz="sm" fw={700}>
Properties
</Text>
<Paper style={{ overflow: 'hidden' }} radius="md" withBorder>
{advertisement.propertyValues.map((value, idx) => (
<Paper
key={idx}
bg={idx % 2 === 1 ? 'transparent' : '#eeeeee'}
radius={0}
p="xs"
>
<SimpleGrid cols={2}>
<Text fw={700}>{value.property_name}</Text>
<Text>{value.option_name}</Text>
</SimpleGrid>
</Paper>
))}
</Paper>
</Stack>
</Stack>
))
.pending(() => 'Loading advertisement...')
.catch((err) => err.message)
.unwrap()}
</PageContainer>
);
}
import { getImageUrl } from '@/apis/ads/images';
import PageContainer from '@/components/page/Container';
import useCurrencyFormatter from '@/hooks/useCurrencyFormatter';
import useTypedParams from '@/hooks/useTypedParams';
import useAdvertisements from '@/stores/useAdvertisements';
import { Carousel } from '@mantine/carousel';
import {
Image,
AspectRatio,
Paper,
Text,
Badge,
Stack,
Group,
SimpleGrid,
Skeleton,
} from '@mantine/core';
import { useEffect } from 'react';
import { MdPerson } from 'react-icons/md';
import { z } from 'zod';

const PARAMS_SCHEMA = z.object({
uid: z.string().uuid(),
});

export default function AdPage() {
const { uid } = useTypedParams(PARAMS_SCHEMA) ?? {};
const { advertisement, getAdvertisement } = useAdvertisements();
const currencyFormatter = useCurrencyFormatter(
advertisement.unwrapValue()?.currency ?? 'EUR',
);

useEffect(() => {
if (!uid) return;
getAdvertisement(uid);
}, [getAdvertisement, uid]);

return (
<PageContainer maw="1000px">
{advertisement
.map((advertisement) => (
<Stack gap="md">
<Carousel
w="100%"
slideSize={{ base: '100%', sm: '50%' }}
slideGap={{ base: 0, sm: 'md' }}
align="start"
withControls
withIndicators
>
{advertisement.images.map((img, idx) => (
<Carousel.Slide key={idx}>
<Paper h="100%" pos="relative" withBorder>
<AspectRatio ratio={4 / 3}>
<Image
src={getImageUrl(img)}
radius="lg"
fallbackSrc="/placeholder-ad-img.webp"
/>
</AspectRatio>
</Paper>
</Carousel.Slide>
))}
</Carousel>
<Stack gap="sm">
<Group>
<Badge size="xl">
{currencyFormatter.format(advertisement.price)}
</Badge>
<Text component="h1" fz="2rem" fw={600}>
{advertisement.title}
</Text>
</Group>
<Group gap="sm" fz="md">
<MdPerson />
<Text>{advertisement.user.name}</Text>
</Group>
</Stack>
<Stack gap="sm">
<Text fz="sm" fw={700}>
Description
</Text>
<Text style={{ whiteSpace: 'pre' }}>
{advertisement.description}
</Text>
</Stack>
<Stack gap="sm">
<Text fz="sm" fw={700}>
Properties
</Text>
<Paper style={{ overflow: 'hidden' }} radius="md" withBorder>
{advertisement.propertyValues.map((value, idx) => (
<Paper
key={idx}
bg={idx % 2 === 1 ? 'transparent' : '#eeeeee'}
radius={0}
p="xs"
>
<SimpleGrid cols={2}>
<Text fw={700}>{value.property_name}</Text>
<Text>{value.option_name}</Text>
</SimpleGrid>
</Paper>
))}
</Paper>
</Stack>
</Stack>
))
.pending(() => (
<Stack gap="md">
<Carousel
w="100%"
slideSize={{ base: '100%', sm: '50%' }}
slideGap={{ base: 0, sm: 'md' }}
align="start"
>
{Array(3)
.fill(true)
.map((_, idx) => (
<Carousel.Slide key={idx}>
<AspectRatio ratio={4 / 3}>
<Skeleton height="100%" />
</AspectRatio>
</Carousel.Slide>
))}
</Carousel>
<Stack gap="sm">
<Skeleton height="1.5em" />
<Skeleton width="15%" />
</Stack>
<Stack gap="sm">
<Skeleton width="15%" />
<Skeleton height="10rem" />
</Stack>
<Stack gap="sm">
<Skeleton width="15%" />
<Skeleton height="15rem" />
</Stack>
</Stack>
))
.catch((err) => err.message)
.unwrap()}
</PageContainer>
);
}
9 changes: 9 additions & 0 deletions apps/frontend/src/pages/components/SkeletonCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AspectRatio, Skeleton } from "@mantine/core";

export default function SkeletonCard() {
return (
<AspectRatio ratio={4 / 3}>
<Skeleton height="100%" />
</AspectRatio>
);
}
Loading

0 comments on commit 8f9b682

Please sign in to comment.