(app-impress) create feature pad

Create a new feature pad for the app impress.
It will be used to display a pad in the app.
This commit is contained in:
Anthony LC
2024-04-03 11:27:54 +02:00
committed by Anthony LC
parent 1cb138ecb3
commit b517b0bc39
7 changed files with 205 additions and 0 deletions

View File

@@ -0,0 +1 @@
export * from './usePad';

View File

@@ -0,0 +1,32 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Pad } from '../types';
export type PadParams = {
id: string;
};
export const getPad = async ({ id }: PadParams): Promise<Pad> => {
const response = await fetchAPI(`pads/${id}`);
if (!response.ok) {
throw new APIError('Failed to get the pad', await errorCauses(response));
}
return response.json() as Promise<Pad>;
};
export const KEY_PAD = 'pad';
export function usePad(
param: PadParams,
queryConfig?: UseQueryOptions<Pad, APIError, Pad>,
) {
return useQuery<Pad, APIError, Pad>({
queryKey: [KEY_PAD, param],
queryFn: () => getPad(param),
...queryConfig,
});
}

View File

@@ -0,0 +1,90 @@
import { DateTime, DateTimeFormatOptions } from 'luxon';
import React from 'react';
import { useTranslation } from 'react-i18next';
import IconGroup from '@/assets/icons/icon-group2.svg';
import { Box, Card, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Pad, Role } from '../types';
const format: DateTimeFormatOptions = {
month: '2-digit',
day: '2-digit',
year: 'numeric',
};
interface PadInfoProps {
pad: Pad;
currentRole: Role;
}
export const PadInfo = ({ pad }: PadInfoProps) => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { i18n } = useTranslation();
const created_at = DateTime.fromISO(pad.created_at)
.setLocale(i18n.language)
.toLocaleString(format);
const updated_at = DateTime.fromISO(pad.updated_at)
.setLocale(i18n.language)
.toLocaleString(format);
return (
<>
<Card className="m-b" style={{ paddingBottom: 0 }}>
<Box className="m-b" $direction="row" $align="center" $gap="1.5rem">
<IconGroup
width={44}
color={colorsTokens()['primary-text']}
aria-label={t('icon group')}
style={{
flexShrink: 0,
alignSelf: 'start',
}}
/>
<Box>
<Text as="h3" $weight="bold" $size="1.25rem" className="mt-0">
{t('Members of “{{padName}}“', {
padName: pad.name,
})}
</Text>
<Text $size="m">
{t('Add people to the “{{padName}}“ group.', {
padName: pad.name,
})}
</Text>
</Box>
</Box>
<Box
className="p-s"
$gap="3rem"
$direction="row"
$justify="start"
$css={`
border-top: 1px solid ${colorsTokens()['card-border']};
padding-left: 1.5rem;
`}
>
<Text $size="s" as="p">
{t('{{count}} member', { count: pad.accesses.length })}
</Text>
<Text $size="s" $display="inline" as="p">
{t('Created at')}&nbsp;
<Text $weight="bold" $display="inline">
{created_at}
</Text>
</Text>
<Text $size="s" $display="inline" as="p">
{t('Last update at')}&nbsp;
<Text $weight="bold" $display="inline">
{updated_at}
</Text>
</Text>
</Box>
</Card>
</>
);
};

View File

@@ -0,0 +1 @@
export * from './PadInfo';

View File

@@ -0,0 +1,3 @@
export * from './api';
export * from './components';
export * from './types';

View File

@@ -0,0 +1,22 @@
import { Access } from '@/features/members';
export enum Role {
MEMBER = 'member',
ADMIN = 'administrator',
OWNER = 'owner',
}
export interface Pad {
id: string;
name: string;
accesses: Access[];
created_at: string;
updated_at: string;
abilities: {
delete: boolean;
get: boolean;
manage_accesses: boolean;
patch: boolean;
put: boolean;
};
}

View File

@@ -0,0 +1,56 @@
import { Loader } from '@openfun/cunningham-react';
import { useRouter as useNavigate } from 'next/navigation';
import { useRouter } from 'next/router';
import { ReactElement } from 'react';
import { Box } from '@/components';
import { TextErrors } from '@/components/TextErrors';
import { usePad } from '@/features/pads';
import { PadLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
const {
query: { id },
} = useRouter();
if (typeof id !== 'string') {
throw new Error('Invalid pad id');
}
return <Pad id={id} />;
};
interface PadProps {
id: string;
}
const Pad = ({ id }: PadProps) => {
const { data: pad, isLoading, isError, error } = usePad({ id });
const navigate = useNavigate();
if (isError && error) {
if (error.status === 404) {
navigate.replace(`/404`);
return null;
}
return <TextErrors causes={error.cause} />;
}
if (isLoading || !pad) {
return (
<Box $align="center" $justify="center" $height="100%">
<Loader />
</Box>
);
}
return <>Toto</>;
};
Page.getLayout = function getLayout(page: ReactElement) {
return <PadLayout>{page}</PadLayout>;
};
export default Page;