✨(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:
@@ -0,0 +1 @@
|
|||||||
|
export * from './usePad';
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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')}
|
||||||
|
<Text $weight="bold" $display="inline">
|
||||||
|
{created_at}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<Text $size="s" $display="inline" as="p">
|
||||||
|
{t('Last update at')}
|
||||||
|
<Text $weight="bold" $display="inline">
|
||||||
|
{updated_at}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './PadInfo';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './api';
|
||||||
|
export * from './components';
|
||||||
|
export * from './types';
|
||||||
22
src/frontend/apps/impress/src/features/pads/pad/types.tsx
Normal file
22
src/frontend/apps/impress/src/features/pads/pad/types.tsx
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
56
src/frontend/apps/impress/src/pages/pads/[id].tsx
Normal file
56
src/frontend/apps/impress/src/pages/pads/[id].tsx
Normal 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;
|
||||||
Reference in New Issue
Block a user