[DEV] correct models

This commit is contained in:
Edouard DUPIN 2025-02-10 19:40:27 +01:00
parent 44b4fa37d3
commit 332c65360d
73 changed files with 1431 additions and 1283 deletions

View File

@ -1,6 +0,0 @@
{
"display": "2025-02-09",
"version": "0.0.1-dev\n - 2025-02-09T21:15:14+01:00",
"commit": "0.0.1-dev\n",
"date": "2025-02-09T21:15:14+01:00"
}

View File

@ -1,25 +0,0 @@
const dayjs = require('dayjs');
const fs = require('fs');
const generateAppBuild = () => {
const getVersion = () => fs.readFileSync('version.txt', 'utf8');
const commit = process.env.VERCEL_GIT_COMMIT_SHA
? process.env.VERCEL_GIT_COMMIT_SHA
: getVersion();
const appBuildContent = {
display: `${dayjs().format('YYYY-MM-DD')}`,
version: `${commit} - ${dayjs().format()}`,
commit,
date: dayjs().format(),
};
fs.writeFileSync(
'./app-build.json',
JSON.stringify(appBuildContent, null, 2)
);
};
generateAppBuild();

View File

@ -7,11 +7,6 @@ const config: KnipConfig = {
// Related to tests // Related to tests
'tests/**', 'tests/**',
'**.conf.js', '**.conf.js',
'steps.d.ts',
'steps_file.js',
'env_ci/codecept.conf.js',
// Generic components are useful.
'src/components/**',
], ],
}; };

View File

@ -18,7 +18,7 @@
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest watch", "test:watch": "vitest watch",
"build": "tsc && vite build", "build": "tsc && vite build",
"static:build": "node build.js && pnpm build", "static:build": "pnpm build",
"dev": "vite", "dev": "vite",
"pretty": "prettier -w .", "pretty": "prettier -w .",
"lint": "pnpm tsc --noEmit", "lint": "pnpm tsc --noEmit",

View File

@ -1,9 +1,9 @@
import { EnvDevelopment } from './components/EnvDevelopment/EnvDevelopment';
import { ErrorBoundary } from '@/errors/ErrorBoundary'; import { ErrorBoundary } from '@/errors/ErrorBoundary';
import { AppRoutes } from '@/scene/AppRoutes'; import { AppRoutes } from '@/scene/AppRoutes';
import { ServiceContextProvider } from '@/service/ServiceContext'; import { ServiceContextProvider } from '@/service/ServiceContext';
import { EnvDevelopment } from './components/EnvDevelopment/EnvDevelopment';
export const App = () => { export const App = () => {
return ( return (
<ServiceContextProvider> <ServiceContextProvider>

View File

@ -3,13 +3,14 @@ import { ReactElement, useEffect, useState } from 'react';
import { Box, BoxProps, Flex, FlexProps } from '@chakra-ui/react'; import { Box, BoxProps, Flex, FlexProps } from '@chakra-ui/react';
import { Image } from '@chakra-ui/react'; import { Image } from '@chakra-ui/react';
import { DataUrlAccess } from '@/utils/data-url-access';
import { Icon } from './Icon';
import { ObjectId } from '@/back-api'; import { ObjectId } from '@/back-api';
import { DataUrlAccess } from '@/utils/data-url-access';
export type CoversProps = Omit<BoxProps, "iconEmpty"> & { import { Icon } from './Icon';
export type CoversProps = Omit<BoxProps, 'iconEmpty'> & {
data?: ObjectId[]; data?: ObjectId[];
size?: BoxProps["width"]; size?: BoxProps['width'];
iconEmpty?: ReactElement; iconEmpty?: ReactElement;
slideshow?: boolean; slideshow?: boolean;
}; };
@ -33,7 +34,9 @@ export const Covers = ({
setPreviousImageIndex(currentImageIndex); setPreviousImageIndex(currentImageIndex);
setTopOpacity(0.0); setTopOpacity(0.0);
setTimeout(() => { setTimeout(() => {
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % (data?.length ?? 1)); setCurrentImageIndex(
(prevIndex) => (prevIndex + 1) % (data?.length ?? 1)
);
setTopOpacity(1.0); setTopOpacity(1.0);
}, 1500); }, 1500);
}, 3000); }, 3000);
@ -60,40 +63,50 @@ export const Covers = ({
} }
if (slideshow === false || data.length === 1) { if (slideshow === false || data.length === 1) {
const url = DataUrlAccess.getThumbnailUrl(data[0]); const url = DataUrlAccess.getThumbnailUrl(data[0]);
return <Image loading="lazy" src={url} maxWidth={size} boxSize={size} /*{...rest}*/ />; return (
<Image
loading="lazy"
src={url}
maxWidth={size}
boxSize={size} /*{...rest}*/
/>
);
} }
const urlCurrent = DataUrlAccess.getThumbnailUrl(data[currentImageIndex]); const urlCurrent = DataUrlAccess.getThumbnailUrl(data[currentImageIndex]);
const urlPrevious = DataUrlAccess.getThumbnailUrl(data[previousImageIndex]); const urlPrevious = DataUrlAccess.getThumbnailUrl(data[previousImageIndex]);
return <Flex return (
position="relative" <Flex
// {...rest} position="relative"
maxWidth={size} // {...rest}
width={size} maxWidth={size}
height={size} width={size}
overflow="hidden"> height={size}
<Image overflow="hidden"
src={urlPrevious} >
loading="lazy" <Image
position="absolute" src={urlPrevious}
top="0" loading="lazy"
left="0" position="absolute"
width="100%" top="0"
height="100%" left="0"
zIndex={1} width="100%"
boxSize={size} height="100%"
/> zIndex={1}
<Image boxSize={size}
src={urlCurrent} />
loading="lazy" <Image
position="absolute" src={urlCurrent}
top="0" loading="lazy"
left="0" position="absolute"
width="100%" top="0"
height="100%" left="0"
boxSize={size} width="100%"
transition="opacity 0.5s ease-in-out" height="100%"
opacity={topOpacity} boxSize={size}
zIndex={2} transition="opacity 0.5s ease-in-out"
/> opacity={topOpacity}
</Flex> zIndex={2}
/>
</Flex>
);
}; };

View File

@ -6,8 +6,8 @@ export const EmptyEnd = () => {
width="full" width="full"
height="25%" height="25%"
minHeight="250px" minHeight="250px"
// borderWidth="1px" // borderWidth="1px"
// borderColor="red" // borderColor="red"
></Box> ></Box>
); );
}; };

View File

@ -1,40 +1,39 @@
import { import {
Box, Box,
Button, Button,
createListCollection,
Dialog, Dialog,
Select, Select,
Span, Span,
Stack, Stack,
Text, Text,
createListCollection,
useDisclosure, useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useSessionService } from '@/service/session';
import { useLogin } from '@/scene/connection/useLogin'; import { useLogin } from '@/scene/connection/useLogin';
import { useSessionService } from '@/service/session';
export const USERS_COLLECTION = createListCollection({ export const USERS_COLLECTION = createListCollection({
items: [ items: [
{ label: "karadmin", value: "adminA@666" }, { label: 'karadmin', value: 'adminA@666' },
{ label: "karuser", value: "userA@666" }, { label: 'karuser', value: 'userA@666' },
{ label: "NO_USER", value: "" }, { label: 'NO_USER', value: '' },
], ],
}) });
export const EnvDevelopment = () => { export const EnvDevelopment = () => {
const dialog = useDisclosure(); const dialog = useDisclosure();
const {clearToken} = useSessionService(); const { clearToken } = useSessionService();
const {connect, lastError} = useLogin(); const { connect, lastError } = useLogin();
const buildEnv = const buildEnv =
process.env.NODE_ENV === 'development' process.env.NODE_ENV === 'development'
? 'Development' ? 'Development'
: import.meta.env.VITE_DEV_ENV_NAME; : import.meta.env.VITE_DEV_ENV_NAME;
const envName: Array<string> = []; const envName: Array<string> = [];
!!buildEnv && envName.push(buildEnv); !!buildEnv && envName.push(buildEnv);
if (!envName.length) { if (!envName.length) {
return null; return null;
} }
@ -77,23 +76,32 @@ export const EnvDevelopment = () => {
> >
{envName.join(' : ')} {envName.join(' : ')}
</Text> </Text>
</Box > </Box>
<Dialog.Root open={dialog.open} onOpenChange={dialog.onClose}> <Dialog.Root open={dialog.open} onOpenChange={dialog.onClose}>
<Dialog.Positioner> <Dialog.Positioner>
<Dialog.Backdrop/> <Dialog.Backdrop />
<Dialog.Content> <Dialog.Content>
<Dialog.Header>Development tools</Dialog.Header> <Dialog.Header>Development tools</Dialog.Header>
<Dialog.Body> <Dialog.Body>
<Stack> <Stack>
<Text>User <Span color="red" fontWeight="bold">{lastError}</Span></Text> <Text>
User{' '}
<Span color="red" fontWeight="bold">
{lastError}
</Span>
</Text>
<Select.Root collection={USERS_COLLECTION}> <Select.Root collection={USERS_COLLECTION}>
<Select.Trigger> <Select.Trigger>
<Select.ValueText placeholder="Select test user" /> <Select.ValueText placeholder="Select test user" />
</Select.Trigger> </Select.Trigger>
<Select.Content> <Select.Content>
{USERS_COLLECTION.items.map((value) => ( {USERS_COLLECTION.items.map((value) => (
<Select.Item item={value} key={value.value}onClick={()=>handleChange(value.label, value.value)} > <Select.Item
item={value}
key={value.value}
onClick={() => handleChange(value.label, value.value)}
>
{value.label} {value.label}
</Select.Item> </Select.Item>
))} ))}
@ -110,4 +118,3 @@ export const EnvDevelopment = () => {
</> </>
); );
}; };

View File

@ -1,41 +1,40 @@
import { import { ReactNode, forwardRef } from 'react';
Box,
Flex, import { Box, Flex, FlexProps } from '@chakra-ui/react';
FlexProps,
} from '@chakra-ui/react';
import { forwardRef, ReactNode } from 'react';
export type IconProps = FlexProps & { export type IconProps = FlexProps & {
children: ReactNode; children: ReactNode;
color?: string; color?: string;
sizeIcon?: FlexProps['width']; sizeIcon?: FlexProps['width'];
}; };
export const Icon = forwardRef<HTMLDivElement, IconProps>( export const Icon = forwardRef<HTMLDivElement, IconProps>(
({ children, color, sizeIcon = '1em', ...rest }, ref) => { ({ children, color, sizeIcon = '1em', ...rest }, ref) => {
return ( return (
<Flex flex="none" <Flex
minWidth={sizeIcon} flex="none"
minHeight={sizeIcon} minWidth={sizeIcon}
maxWidth={sizeIcon} minHeight={sizeIcon}
maxHeight={sizeIcon} maxWidth={sizeIcon}
align="center" maxHeight={sizeIcon}
padding="1px" align="center"
ref={ref} padding="1px"
{...rest}> ref={ref}
<Box {...rest}
marginX="auto" >
width="100%" <Box
minWidth="100%" marginX="auto"
height="100%" width="100%"
color={color} minWidth="100%"
asChild height="100%"
> color={color}
{children} asChild
</Box> >
</Flex> {children}
); </Box>
} </Flex>
);
}
); );
Icon.displayName = 'Icon'; Icon.displayName = 'Icon';

View File

@ -4,8 +4,8 @@ import { Flex, FlexProps } from '@chakra-ui/react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { PageLayout } from '@/components/Layout/PageLayout'; import { PageLayout } from '@/components/Layout/PageLayout';
import { colors } from '@/theme/colors';
import { useColorModeValue } from '@/components/ui/color-mode'; import { useColorModeValue } from '@/components/ui/color-mode';
import { colors } from '@/theme/colors';
export type LayoutProps = FlexProps & { export type LayoutProps = FlexProps & {
children: ReactNode; children: ReactNode;

View File

@ -1,22 +1,20 @@
export { export {
ParameterLayoutContent as Content, ParameterLayoutContent as Content,
type ParameterLayoutContentProps as ContentProps type ParameterLayoutContentProps as ContentProps,
} from './ParameterLayoutContent'; } from './ParameterLayoutContent';
export { export {
ParameterLayoutFooter as Footer, ParameterLayoutFooter as Footer,
type ParameterLayoutFooterProps as FooterProps type ParameterLayoutFooterProps as FooterProps,
} from './ParameterLayoutFooter'; } from './ParameterLayoutFooter';
export { export {
ParameterLayoutHeader as Header, ParameterLayoutHeader as Header,
type ParameterLayoutHeaderProps as HeaderProps type ParameterLayoutHeaderProps as HeaderProps,
} from './ParameterLayoutHeader'; } from './ParameterLayoutHeader';
export { export {
ParameterLayoutHeaderBase as HeaderBase, ParameterLayoutHeaderBase as HeaderBase,
type ParameterLayoutHeaderBaseProps as HeaderBaseProps type ParameterLayoutHeaderBaseProps as HeaderBaseProps,
} from './ParameterLayoutHeaderBase'; } from './ParameterLayoutHeaderBase';
export { export {
ParameterLayoutRoot as Root, ParameterLayoutRoot as Root,
type ParameterLayoutRootProps as RootProps type ParameterLayoutRootProps as RootProps,
} from './ParameterLayoutRoot'; } from './ParameterLayoutRoot';

View File

@ -1,19 +1,25 @@
import { Flex } from "@chakra-ui/react"; import { ReactNode } from 'react';
import { ReactNode } from "react";
import { Flex } from '@chakra-ui/react';
export type ParameterLayoutContentProps = { export type ParameterLayoutContentProps = {
children?:ReactNode; children?: ReactNode;
} };
export const ParameterLayoutContent = ({children}:ParameterLayoutContentProps) => { export const ParameterLayoutContent = ({
return <Flex children,
}: ParameterLayoutContentProps) => {
return (
<Flex
direction="column" direction="column"
width="full" width="full"
borderY="1px solid black" borderY="1px solid black"
paddingY="15px" paddingY="15px"
paddingX="25px" paddingX="25px"
minHeight="10px" minHeight="10px"
background="gray.700"> background="gray.700"
{children} >
{children}
</Flex> </Flex>
} );
};

View File

@ -1,16 +1,17 @@
import { Flex } from "@chakra-ui/react"; import { ReactNode } from 'react';
import { ReactNode } from "react";
import { Flex } from '@chakra-ui/react';
export type ParameterLayoutFooterProps = { export type ParameterLayoutFooterProps = {
children?: ReactNode; children?: ReactNode;
} };
export const ParameterLayoutFooter = ({children}:ParameterLayoutFooterProps) => { export const ParameterLayoutFooter = ({
return <Flex children,
width="full" }: ParameterLayoutFooterProps) => {
paddingY="15px" return (
paddingX="25px" <Flex width="full" paddingY="15px" paddingX="25px" minHeight="10px">
minHeight="10px"> {children}
{children}
</Flex> </Flex>
} );
};

View File

@ -1,17 +1,17 @@
import { Flex } from "@chakra-ui/react"; import { ReactNode } from 'react';
import { ReactNode } from "react";
import { Flex } from '@chakra-ui/react';
export type ParameterLayoutHeaderProps = { export type ParameterLayoutHeaderProps = {
children?:ReactNode; children?: ReactNode;
} };
export const ParameterLayoutHeader = ({children}:ParameterLayoutHeaderProps) => { export const ParameterLayoutHeader = ({
return <Flex children,
width="full" }: ParameterLayoutHeaderProps) => {
paddingY="15px" return (
paddingX="25px" <Flex width="full" paddingY="15px" paddingX="25px" minHeight="10px">
minHeight="10px" {children}
>
{children}
</Flex> </Flex>
} );
};

View File

@ -1,23 +1,24 @@
import { Flex, Text } from "@chakra-ui/react"; import { Flex, Text } from '@chakra-ui/react';
import { ParameterLayoutHeader } from "./ParameterLayoutHeader";
import { ParameterLayoutHeader } from './ParameterLayoutHeader';
export type ParameterLayoutHeaderBaseProps = { export type ParameterLayoutHeaderBaseProps = {
title:string; title: string;
description?: string, description?: string;
} };
export const ParameterLayoutHeaderBase = ({ export const ParameterLayoutHeaderBase = ({
title, title,
description, description,
}:ParameterLayoutHeaderBaseProps) => { }: ParameterLayoutHeaderBaseProps) => {
return <ParameterLayoutHeader> return (
<Flex direction="column"> <ParameterLayoutHeader>
<Text <Flex direction="column">
fontSize="25px" <Text fontSize="25px" fontWeight="bold">
fontWeight="bold"> {title}
{title}
</Text> </Text>
{description && <Text>{description}</Text>} {description && <Text>{description}</Text>}
</Flex> </Flex>
</ParameterLayoutHeader> </ParameterLayoutHeader>
} );
};

View File

@ -1,17 +1,24 @@
import { VStack } from '@chakra-ui/react';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
export type ParameterLayoutRootProps = { import { VStack } from '@chakra-ui/react';
children?: ReactNode;
}
export const ParameterLayoutRoot = ({children}:ParameterLayoutRootProps) => { export type ParameterLayoutRootProps = {
return <VStack gap="0px" marginX="15%" marginY="20px" justify="center" children?: ReactNode;
//borderRadius="20px" };
borderRadius="0px"
border="1px solid black" export const ParameterLayoutRoot = ({ children }: ParameterLayoutRootProps) => {
background="gray.500"> return (
{children} <VStack
gap="0px"
marginX="15%"
marginY="20px"
justify="center"
//borderRadius="20px"
borderRadius="0px"
border="1px solid black"
background="gray.500"
>
{children}
</VStack> </VStack>
} );
};

View File

@ -1,9 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { import { Group, Input } from '@chakra-ui/react';
Group,
Input,
} from '@chakra-ui/react';
import { MdSearch } from 'react-icons/md'; import { MdSearch } from 'react-icons/md';
export type SearchInputProps = { export type SearchInputProps = {

View File

@ -2,15 +2,15 @@ import { ReactNode } from 'react';
import { import {
Box, Box,
Button,
ConditionalValue,
Flex, Flex,
HStack, HStack,
IconButton, IconButton,
Text,
useDisclosure,
Button,
ConditionalValue,
Span, Span,
Text,
chakra, chakra,
useDisclosure,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
LuAlignJustify, LuAlignJustify,
@ -22,13 +22,18 @@ import {
LuSettings, LuSettings,
LuSun, LuSun,
} from 'react-icons/lu'; } from 'react-icons/lu';
import {
MdHelp,
MdHome,
MdKey,
MdMore,
MdOutlineDashboardCustomize,
MdOutlineGroup,
MdSettings,
MdSupervisedUserCircle,
} from 'react-icons/md';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useServiceContext } from '@/service/ServiceContext';
import { colors } from '@/theme/colors';
import { useSessionService } from '@/service/session';
import { MdHelp, MdHome, MdKey, MdMore, MdOutlineDashboardCustomize, MdOutlineGroup, MdSettings, MdSupervisedUserCircle } from 'react-icons/md';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
import { useColorMode, useColorModeValue } from '@/components/ui/color-mode'; import { useColorMode, useColorModeValue } from '@/components/ui/color-mode';
import { import {
DrawerBody, DrawerBody,
@ -36,47 +41,68 @@ import {
DrawerHeader, DrawerHeader,
DrawerRoot, DrawerRoot,
} from '@/components/ui/drawer'; } from '@/components/ui/drawer';
import {
MenuContent,
MenuItem,
MenuRoot,
MenuTrigger,
} from '@/components/ui/menu';
import { useServiceContext } from '@/service/ServiceContext';
import { useSessionService } from '@/service/session';
import { colors } from '@/theme/colors';
export const TOP_BAR_HEIGHT = '50px'; export const TOP_BAR_HEIGHT = '50px';
export const BUTTON_TOP_BAR_PROPERTY = { export const BUTTON_TOP_BAR_PROPERTY = {
variant: "ghost" as ConditionalValue<"ghost" | "outline" | "solid" | "subtle" | "surface" | "plain" | undefined>, variant: 'ghost' as ConditionalValue<
'ghost' | 'outline' | 'solid' | 'subtle' | 'surface' | 'plain' | undefined
>,
//colorPalette: "brand", //colorPalette: "brand",
fontSize: '20px', fontSize: '20px',
textTransform: 'uppercase', textTransform: 'uppercase',
height: TOP_BAR_HEIGHT, height: TOP_BAR_HEIGHT,
}; };
export type TopBarProps = { export type TopBarProps = {
children?: ReactNode; children?: ReactNode;
title?: string; title?: string;
}; };
const ButtonMenuLeft = ({ dest, title, icon, const ButtonMenuLeft = ({
onClickEnd = () => { }, }: { dest,
dest: string, title: string, icon: ReactNode title,
onClickEnd?: () => void; icon,
}) => { onClickEnd = () => {},
}: {
dest: string;
title: string;
icon: ReactNode;
onClickEnd?: () => void;
}) => {
const navigate = useNavigate(); const navigate = useNavigate();
return <> return (
<Button <>
background="#00000000" <Button
borderRadius="0px" background="#00000000"
onClick={() => { borderRadius="0px"
navigate(dest); onClick={() => {
onClickEnd(); navigate(dest);
}} onClickEnd();
width="full" }}
{...BUTTON_TOP_BAR_PROPERTY} width="full"
> {...BUTTON_TOP_BAR_PROPERTY}
<Box asChild style={{ width: "45px", height: "45px" }}>{icon}</Box> >
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto"> <Box asChild style={{ width: '45px', height: '45px' }}>
{title} {icon}
</Text> </Box>
</Button> <Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
<Box marginY="5" marginX="10" height="2px" background="brand.600" /> {title}
</> </Text>
} </Button>
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
</>
);
};
export const TopBar = ({ title, children }: TopBarProps) => { export const TopBar = ({ title, children }: TopBarProps) => {
const { colorMode, toggleColorMode } = useColorMode(); const { colorMode, toggleColorMode } = useColorMode();
@ -138,7 +164,10 @@ export const TopBar = ({ title, children }: TopBarProps) => {
<Flex right="0"> <Flex right="0">
{!session?.token && ( {!session?.token && (
<> <>
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={() => navigate('/login')}> <Button
{...BUTTON_TOP_BAR_PROPERTY}
onClick={() => navigate('/login')}
>
<LuLogIn /> <LuLogIn />
<Text paddingLeft="3px" fontWeight="bold"> <Text paddingLeft="3px" fontWeight="bold">
Sign-in Sign-in
@ -163,24 +192,55 @@ export const TopBar = ({ title, children }: TopBarProps) => {
//asChild //asChild
{...BUTTON_TOP_BAR_PROPERTY} {...BUTTON_TOP_BAR_PROPERTY}
width={TOP_BAR_HEIGHT} width={TOP_BAR_HEIGHT}
><LuCircleUserRound/></IconButton> >
<LuCircleUserRound />
</IconButton>
</MenuTrigger> </MenuTrigger>
<MenuContent> <MenuContent>
<MenuItem value="user" valueText="user" color={useColorModeValue('brand.800', 'brand.200')}> <MenuItem
value="user"
valueText="user"
color={useColorModeValue('brand.800', 'brand.200')}
>
<MdSupervisedUserCircle /> <MdSupervisedUserCircle />
<Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box> <Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box>
</MenuItem> </MenuItem>
<MenuItem value="Settings" valueText="Settings" onClick={() => navigate('/settings')}><LuSettings />Settings</MenuItem> <MenuItem
<MenuItem value="Help" valueText="Help" onClick={() => navigate('/help')}><MdHelp /> Help</MenuItem> value="Settings"
<MenuItem value="Sign-out" valueText="Sign-out" onClick={() => navigate('/signout')}> valueText="Settings"
onClick={() => navigate('/settings')}
>
<LuSettings />
Settings
</MenuItem>
<MenuItem
value="Help"
valueText="Help"
onClick={() => navigate('/help')}
>
<MdHelp /> Help
</MenuItem>
<MenuItem
value="Sign-out"
valueText="Sign-out"
onClick={() => navigate('/signout')}
>
<LuLogOut /> Sign-out <LuLogOut /> Sign-out
</MenuItem> </MenuItem>
{colorMode === 'light' ? ( {colorMode === 'light' ? (
<MenuItem value="set-dark" valueText="set-dark" onClick={toggleColorMode}> <MenuItem
value="set-dark"
valueText="set-dark"
onClick={toggleColorMode}
>
<LuMoon /> Set dark mode <LuMoon /> Set dark mode
</MenuItem> </MenuItem>
) : ( ) : (
<MenuItem value="set-light" valueText="set-light" onClick={toggleColorMode}> <MenuItem
value="set-light"
valueText="set-light"
onClick={toggleColorMode}
>
<LuSun /> Set light mode <LuSun /> Set light mode
</MenuItem> </MenuItem>
)} )}
@ -194,8 +254,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
open={drawerDisclose.open} open={drawerDisclose.open}
data-testid="top-bar_drawer-root" data-testid="top-bar_drawer-root"
> >
<DrawerContent <DrawerContent data-testid="top-bar_drawer-content">
data-testid="top-bar_drawer-content">
<DrawerHeader <DrawerHeader
paddingY="auto" paddingY="auto"
as="button" as="button"
@ -205,24 +264,46 @@ export const TopBar = ({ title, children }: TopBarProps) => {
color={useColorModeValue('brand.900', 'brand.50')} color={useColorModeValue('brand.900', 'brand.50')}
textTransform="uppercase" textTransform="uppercase"
> >
<HStack <HStack {...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
{...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
<LuArrowBigLeft /> <LuArrowBigLeft />
<Span paddingLeft="3px"> <Span paddingLeft="3px">karso</Span>
karso
</Span>
</HStack> </HStack>
</DrawerHeader> </DrawerHeader>
<DrawerBody paddingX="0px"> <DrawerBody paddingX="0px">
<Box marginY="3" /> <Box marginY="3" />
<ButtonMenuLeft onClickEnd={drawerDisclose.onClose} dest="/" title="Home" icon={<MdHome />} /> <ButtonMenuLeft
<ButtonMenuLeft onClickEnd={drawerDisclose.onClose} dest="/change-password" title="Change password" icon={<MdKey />} /> onClickEnd={drawerDisclose.onClose}
<ButtonMenuLeft onClickEnd={drawerDisclose.onClose} dest="/admin-settings" title="Admin settings" icon={<MdSettings />} /> dest="/"
<ButtonMenuLeft onClickEnd={drawerDisclose.onClose} dest="/manage-account" title="Manage account" icon={<MdOutlineGroup />} /> title="Home"
<ButtonMenuLeft onClickEnd={drawerDisclose.onClose} dest="/manage-application" title="Manage application" icon={<MdOutlineDashboardCustomize />} /> icon={<MdHome />}
/>
<ButtonMenuLeft
onClickEnd={drawerDisclose.onClose}
dest="/change-password"
title="Change password"
icon={<MdKey />}
/>
<ButtonMenuLeft
onClickEnd={drawerDisclose.onClose}
dest="/admin-settings"
title="Admin settings"
icon={<MdSettings />}
/>
<ButtonMenuLeft
onClickEnd={drawerDisclose.onClose}
dest="/manage-account"
title="Manage account"
icon={<MdOutlineGroup />}
/>
<ButtonMenuLeft
onClickEnd={drawerDisclose.onClose}
dest="/manage-application"
title="Manage application"
icon={<MdOutlineDashboardCustomize />}
/>
</DrawerBody> </DrawerBody>
</DrawerContent> </DrawerContent>
</DrawerRoot> </DrawerRoot>
</Flex > </Flex>
); );
}; };

View File

@ -1,9 +1,9 @@
import { ReactNode, useState } from 'react'; import { ReactNode, useState } from 'react';
import { LuMenu } from 'react-icons/lu'; import { LuMenu } from 'react-icons/lu';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../ui/menu';
import { Button } from '../ui/button';
import { Button } from '../ui/button';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../ui/menu';
export type MenuElement = { export type MenuElement = {
icon?: ReactNode; icon?: ReactNode;
@ -20,27 +20,33 @@ export const ContextMenu = ({ elements }: ContextMenuProps) => {
return <></>; return <></>;
} }
return ( return (
<MenuRoot <MenuRoot data-testid="context-menu">
data-testid="context-menu"> <MenuTrigger
<MenuTrigger asChild asChild
marginY="auto" marginY="auto"
marginRight="4px" marginRight="4px"
data-testid="context-menu_trigger"> data-testid="context-menu_trigger"
>
{/* This is very stupid, we need to set as span to prevent a button in button... WTF */} {/* This is very stupid, we need to set as span to prevent a button in button... WTF */}
<Button variant="ghost" color="brand.500"> <Button variant="ghost" color="brand.500">
<LuMenu /> <LuMenu />
</Button> </Button>
</MenuTrigger> </MenuTrigger>
<MenuContent <MenuContent data-testid="context-menu_content">
data-testid="context-menu_content">
{elements?.map((data) => ( {elements?.map((data) => (
<MenuItem key={data.name} value={data.name} onClick={data.onClick} height="65px" fontSize="25px" <MenuItem
data-test-id="context-menu_item" > key={data.name}
value={data.name}
onClick={data.onClick}
height="65px"
fontSize="25px"
data-test-id="context-menu_item"
>
{data.icon} {data.icon}
{data.name} {data.name}
</MenuItem> </MenuItem>
))} ))}
</MenuContent> </MenuContent>
</MenuRoot > </MenuRoot>
); );
}; };

View File

@ -3,6 +3,7 @@ import { RefObject } from 'react';
import { Input } from '@chakra-ui/react'; import { Input } from '@chakra-ui/react';
import { FormGroup, FormGroupProps } from '@/components/form/FormGroup'; import { FormGroup, FormGroupProps } from '@/components/form/FormGroup';
import { useFormidableContextElement } from '../formidable'; import { useFormidableContextElement } from '../formidable';
export type FormInputProps = { export type FormInputProps = {
@ -19,12 +20,9 @@ export const FormInput = ({
placeholder, placeholder,
...rest ...rest
}: FormInputProps) => { }: FormInputProps) => {
const {value, onChange} = useFormidableContextElement(name); const { value, onChange } = useFormidableContextElement(name);
return ( return (
<FormGroup <FormGroup name={name} {...rest}>
name={name}
{...rest}
>
<Input <Input
ref={ref} ref={ref}
type="text" type="text"

View File

@ -4,6 +4,7 @@ import { Box } from '@chakra-ui/react';
import { FormSelect } from '@/components/form/FormSelect'; import { FormSelect } from '@/components/form/FormSelect';
import { useFormidable } from '@/components/formidable/FormidableConfig'; import { useFormidable } from '@/components/formidable/FormidableConfig';
import { Formidable } from '../formidable'; import { Formidable } from '../formidable';
export default { export default {
@ -109,7 +110,7 @@ export const DarkBackground = {
{ id: 222, name: 'Second Item' }, { id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' }, { id: 333, name: 'third item' },
]} ]}
/> />
</Box> </Box>
</Formidable.From> </Formidable.From>
); );

View File

@ -4,6 +4,7 @@ import { Box } from '@chakra-ui/react';
import { FormSelectMultiple } from '@/components/form/FormSelectMultiple'; import { FormSelectMultiple } from '@/components/form/FormSelectMultiple';
import { useFormidable } from '@/components/formidable/FormidableConfig'; import { useFormidable } from '@/components/formidable/FormidableConfig';
import { Formidable } from '../formidable'; import { Formidable } from '../formidable';
export default { export default {
@ -58,7 +59,7 @@ export const ChangeName = () => {
{ id: 222, name: 'Second Item' }, { id: 222, name: 'Second Item' },
{ id: 333, name: 'third item' }, { id: 333, name: 'third item' },
]} ]}
/> />
</Formidable.From> </Formidable.From>
); );
}; };

View File

@ -1,6 +1,8 @@
export {type FormidableConfig as config, export {
useFormidable type FormidableConfig as config,
useFormidable,
} from './FormidableConfig'; } from './FormidableConfig';
export {FormidableForm as From, export {
type FormidableFormProps as FormProps FormidableForm as From,
} from './FormidableForm'; type FormidableFormProps as FormProps,
} from './FormidableForm';

View File

@ -1,11 +1,7 @@
export * as Formidable from './Fromidable'; export * as Formidable from './Fromidable';
export { export {
useFormidableContext, useFormidableContext,
useFormidableContextElement useFormidableContextElement,
} from './FormidableContext' } from './FormidableContext';
export { export { useFormidable } from './FormidableConfig';
useFormidable export { zodResolver } from './utils';
} from './FormidableConfig';
export {
zodResolver
} from './utils';

View File

@ -1,11 +1,15 @@
import { useRef } from 'react'; import { useRef } from 'react';
import { import { Button, UseDisclosureReturn } from '@chakra-ui/react';
Button,
UseDisclosureReturn, import {
} from '@chakra-ui/react'; DialogBody,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
} from '@/components/ui/dialog';
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
export type ConfirmPopUpProps = { export type ConfirmPopUpProps = {
title: string; title: string;
body: string; body: string;
@ -27,7 +31,8 @@ export const ConfirmPopUp = ({
}; };
const cancelRef = useRef<HTMLButtonElement>(null); const cancelRef = useRef<HTMLButtonElement>(null);
return ( return (
<DialogRoot role="alertdialog" <DialogRoot
role="alertdialog"
open={disclosure.open} open={disclosure.open}
//leastDestructiveRef={cancelRef} //leastDestructiveRef={cancelRef}
onOpenChange={disclosure.onClose} onOpenChange={disclosure.onClose}

View File

@ -1,14 +1,14 @@
import { useRef } from 'react'; import { useRef } from 'react';
import { Button, Flex, Progress, Text } from '@chakra-ui/react';
import { import {
Flex, DialogBody,
Progress, DialogContent,
Text, DialogFooter,
Button, DialogHeader,
} from '@chakra-ui/react'; DialogRoot,
} from '@/components/ui/dialog';
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
export type PopUpUploadProgressProps = { export type PopUpUploadProgressProps = {
title: string; title: string;

View File

@ -102,14 +102,20 @@ export const SelectMultiple = ({
const createNewItem = !onCreate const createNewItem = !onCreate
? undefined ? undefined
: (data: string) => { : (data: string) => {
onCreate(data); onCreate(data);
setCurrentSearch(undefined); setCurrentSearch(undefined);
}; };
return ( return (
<Flex direction="column" width="full" gap="0px"> <Flex direction="column" width="full" gap="0px">
{selectedOptions && ( {selectedOptions && (
<HStack wrap="wrap" gap="5px" justify="left" width="full" marginBottom="2px"> <HStack
wrap="wrap"
gap="5px"
justify="left"
width="full"
marginBottom="2px"
>
{selectedOptions.map((data) => ( {selectedOptions.map((data) => (
<Flex align="flex-start" key={data[keyKey]}> <Flex align="flex-start" key={data[keyKey]}>
<Tag.Root <Tag.Root
@ -119,7 +125,10 @@ export const SelectMultiple = ({
backgroundColor="green.800" backgroundColor="green.800"
> >
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label> <Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
<Tag.CloseTrigger boxSize="5" onClick={() => selectValue(data)} /> <Tag.CloseTrigger
boxSize="5"
onClick={() => selectValue(data)}
/>
</Tag.Root> </Tag.Root>
</Flex> </Flex>
))} ))}
@ -134,7 +143,13 @@ export const SelectMultiple = ({
//onSubmit={onSubmit} //onSubmit={onSubmit}
onFocus={() => setShowList(true)} onFocus={() => setShowList(true)}
onBlur={() => setTimeout(() => setShowList(false), 200)} onBlur={() => setTimeout(() => setShowList(false), 200)}
value={showList ? (currentSearch ?? '') : hasSuggestion ? `suggest: ${currentSearch}` : ''} value={
showList
? (currentSearch ?? '')
: hasSuggestion
? `suggest: ${currentSearch}`
: ''
}
borderRadius="5px 0 0 5px" borderRadius="5px 0 0 5px"
/> />
<Button <Button

View File

@ -50,7 +50,9 @@ export const SelectSingle = ({
onCreate ? suggestion : undefined onCreate ? suggestion : undefined
); );
useEffect(() => { useEffect(() => {
console.log(`Update suggestion : ${onCreate} ${suggestion} ==> ${onCreate ? suggestion : undefined} .. ${onCreate && !isNullOrUndefined(suggestion) ? true : false}`); console.log(
`Update suggestion : ${onCreate} ${suggestion} ==> ${onCreate ? suggestion : undefined} .. ${onCreate && !isNullOrUndefined(suggestion) ? true : false}`
);
setCurrentSearch(onCreate ? suggestion : undefined); setCurrentSearch(onCreate ? suggestion : undefined);
setHasSuggestion(onCreate && !isNullOrUndefined(suggestion) ? true : false); setHasSuggestion(onCreate && !isNullOrUndefined(suggestion) ? true : false);
}, [suggestion]); }, [suggestion]);
@ -95,10 +97,10 @@ export const SelectSingle = ({
const createNewItem = !onCreate const createNewItem = !onCreate
? undefined ? undefined
: (data: string) => { : (data: string) => {
onCreate(data); onCreate(data);
setCurrentSearch(undefined); setCurrentSearch(undefined);
setHasSuggestion(false); setHasSuggestion(false);
}; };
return ( return (
<Flex direction="column" width="full" gap="0px"> <Flex direction="column" width="full" gap="0px">
@ -110,7 +112,10 @@ export const SelectSingle = ({
onFocus={() => setShowList(true)} onFocus={() => setShowList(true)}
onBlur={() => setTimeout(() => setShowList(false), 200)} onBlur={() => setTimeout(() => setShowList(false), 200)}
value={ value={
showList ? (currentSearch ?? '') : (selectedOptions?.name ?? (hasSuggestion ? `suggest: ${currentSearch}` : '')) showList
? (currentSearch ?? '')
: (selectedOptions?.name ??
(hasSuggestion ? `suggest: ${currentSearch}` : ''))
} }
backgroundColor={ backgroundColor={
showList || !selectedOptions ? undefined : 'green.800' showList || !selectedOptions ? undefined : 'green.800'

View File

@ -1,24 +1,25 @@
"use client" 'use client';
import type { GroupProps, SlotRecipeProps } from "@chakra-ui/react" import * as React from 'react';
import { Avatar as ChakraAvatar, Group } from "@chakra-ui/react"
import * as React from "react"
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement> import type { GroupProps, SlotRecipeProps } from '@chakra-ui/react';
import { Avatar as ChakraAvatar, Group } from '@chakra-ui/react';
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>;
export interface AvatarProps extends ChakraAvatar.RootProps { export interface AvatarProps extends ChakraAvatar.RootProps {
name?: string name?: string;
src?: string src?: string;
srcSet?: string srcSet?: string;
loading?: ImageProps["loading"] loading?: ImageProps['loading'];
icon?: React.ReactElement icon?: React.ReactElement;
fallback?: React.ReactNode fallback?: React.ReactNode;
} }
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>( export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
function Avatar(props, ref) { function Avatar(props, ref) {
const { name, src, srcSet, loading, icon, fallback, children, ...rest } = const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
props props;
return ( return (
<ChakraAvatar.Root ref={ref} {...rest}> <ChakraAvatar.Root ref={ref} {...rest}>
<AvatarFallback name={name} icon={icon}> <AvatarFallback name={name} icon={icon}>
@ -27,18 +28,18 @@ export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
<ChakraAvatar.Image src={src} srcSet={srcSet} loading={loading} /> <ChakraAvatar.Image src={src} srcSet={srcSet} loading={loading} />
{children} {children}
</ChakraAvatar.Root> </ChakraAvatar.Root>
) );
}, }
) );
interface AvatarFallbackProps extends ChakraAvatar.FallbackProps { interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
name?: string name?: string;
icon?: React.ReactElement icon?: React.ReactElement;
} }
const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>( const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
function AvatarFallback(props, ref) { function AvatarFallback(props, ref) {
const { name, icon, children, ...rest } = props const { name, icon, children, ...rest } = props;
return ( return (
<ChakraAvatar.Fallback ref={ref} {...rest}> <ChakraAvatar.Fallback ref={ref} {...rest}>
{children} {children}
@ -47,28 +48,28 @@ const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
<ChakraAvatar.Icon asChild={!!icon}>{icon}</ChakraAvatar.Icon> <ChakraAvatar.Icon asChild={!!icon}>{icon}</ChakraAvatar.Icon>
)} )}
</ChakraAvatar.Fallback> </ChakraAvatar.Fallback>
) );
}, }
) );
function getInitials(name: string) { function getInitials(name: string) {
const names = name.trim().split(" ") const names = name.trim().split(' ');
const firstName = names[0] != null ? names[0] : "" const firstName = names[0] != null ? names[0] : '';
const lastName = names.length > 1 ? names[names.length - 1] : "" const lastName = names.length > 1 ? names[names.length - 1] : '';
return firstName && lastName return firstName && lastName
? `${firstName.charAt(0)}${lastName.charAt(0)}` ? `${firstName.charAt(0)}${lastName.charAt(0)}`
: firstName.charAt(0) : firstName.charAt(0);
} }
interface AvatarGroupProps extends GroupProps, SlotRecipeProps<"avatar"> {} interface AvatarGroupProps extends GroupProps, SlotRecipeProps<'avatar'> {}
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>( export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
function AvatarGroup(props, ref) { function AvatarGroup(props, ref) {
const { size, variant, borderless, ...rest } = props const { size, variant, borderless, ...rest } = props;
return ( return (
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}> <ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
<Group gap="0" spaceX="-3" ref={ref} {...rest} /> <Group gap="0" spaceX="-3" ref={ref} {...rest} />
</ChakraAvatar.PropsProvider> </ChakraAvatar.PropsProvider>
) );
}, }
) );

View File

@ -1,22 +1,23 @@
import type { ButtonProps as ChakraButtonProps } from "@chakra-ui/react" import * as React from 'react';
import type { ButtonProps as ChakraButtonProps } from '@chakra-ui/react';
import { import {
AbsoluteCenter, AbsoluteCenter,
Button as ChakraButton, Button as ChakraButton,
Span, Span,
Spinner, Spinner,
} from "@chakra-ui/react" } from '@chakra-ui/react';
import * as React from "react"
interface ButtonLoadingProps { interface ButtonLoadingProps {
loading?: boolean loading?: boolean;
loadingText?: React.ReactNode loadingText?: React.ReactNode;
} }
export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {} export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
function Button(props, ref) { function Button(props, ref) {
const { loading, disabled, loadingText, children, ...rest } = props const { loading, disabled, loadingText, children, ...rest } = props;
return ( return (
<ChakraButton disabled={loading || disabled} ref={ref} {...rest}> <ChakraButton disabled={loading || disabled} ref={ref} {...rest}>
{loading && !loadingText ? ( {loading && !loadingText ? (
@ -35,6 +36,6 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
children children
)} )}
</ChakraButton> </ChakraButton>
) );
}, }
) );

View File

@ -1,15 +1,16 @@
import { Checkbox as ChakraCheckbox } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { Checkbox as ChakraCheckbox } from '@chakra-ui/react';
export interface CheckboxProps extends ChakraCheckbox.RootProps { export interface CheckboxProps extends ChakraCheckbox.RootProps {
icon?: React.ReactNode icon?: React.ReactNode;
inputProps?: React.InputHTMLAttributes<HTMLInputElement> inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
rootRef?: React.Ref<HTMLLabelElement> rootRef?: React.Ref<HTMLLabelElement>;
} }
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>( export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
function Checkbox(props, ref) { function Checkbox(props, ref) {
const { icon, children, inputProps, rootRef, ...rest } = props const { icon, children, inputProps, rootRef, ...rest } = props;
return ( return (
<ChakraCheckbox.Root ref={rootRef} {...rest}> <ChakraCheckbox.Root ref={rootRef} {...rest}>
<ChakraCheckbox.HiddenInput ref={ref} {...inputProps} /> <ChakraCheckbox.HiddenInput ref={ref} {...inputProps} />
@ -20,6 +21,6 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
<ChakraCheckbox.Label>{children}</ChakraCheckbox.Label> <ChakraCheckbox.Label>{children}</ChakraCheckbox.Label>
)} )}
</ChakraCheckbox.Root> </ChakraCheckbox.Root>
) );
}, }
) );

View File

@ -1,9 +1,10 @@
import type { ButtonProps } from "@chakra-ui/react" import * as React from 'react';
import { IconButton as ChakraIconButton } from "@chakra-ui/react"
import * as React from "react"
import { LuX } from "react-icons/lu"
export type CloseButtonProps = ButtonProps import type { ButtonProps } from '@chakra-ui/react';
import { IconButton as ChakraIconButton } from '@chakra-ui/react';
import { LuX } from 'react-icons/lu';
export type CloseButtonProps = ButtonProps;
export const CloseButton = React.forwardRef< export const CloseButton = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
@ -13,5 +14,5 @@ export const CloseButton = React.forwardRef<
<ChakraIconButton variant="ghost" aria-label="Close" ref={ref} {...props}> <ChakraIconButton variant="ghost" aria-label="Close" ref={ref} {...props}>
{props.children ?? <LuX />} {props.children ?? <LuX />}
</ChakraIconButton> </ChakraIconButton>
) );
}) });

View File

@ -1,57 +1,58 @@
"use client" 'use client';
import type { IconButtonProps } from "@chakra-ui/react" import * as React from 'react';
import { ClientOnly, IconButton, Skeleton } from "@chakra-ui/react"
import { ThemeProvider, useTheme } from "next-themes" import type { IconButtonProps } from '@chakra-ui/react';
import type { ThemeProviderProps } from "next-themes" import { ClientOnly, IconButton, Skeleton } from '@chakra-ui/react';
import * as React from "react" import { ThemeProvider, useTheme } from 'next-themes';
import { LuMoon, LuSun } from "react-icons/lu" import type { ThemeProviderProps } from 'next-themes';
import { LuMoon, LuSun } from 'react-icons/lu';
export interface ColorModeProviderProps extends ThemeProviderProps {} export interface ColorModeProviderProps extends ThemeProviderProps {}
export function ColorModeProvider(props: ColorModeProviderProps) { export function ColorModeProvider(props: ColorModeProviderProps) {
return ( return (
<ThemeProvider attribute="class" disableTransitionOnChange {...props} /> <ThemeProvider attribute="class" disableTransitionOnChange {...props} />
) );
} }
export type ColorMode = "light" | "dark" export type ColorMode = 'light' | 'dark';
export interface UseColorModeReturn { export interface UseColorModeReturn {
colorMode: ColorMode colorMode: ColorMode;
setColorMode: (colorMode: ColorMode) => void setColorMode: (colorMode: ColorMode) => void;
toggleColorMode: () => void toggleColorMode: () => void;
} }
export function useColorMode(): UseColorModeReturn { export function useColorMode(): UseColorModeReturn {
const { resolvedTheme, setTheme } = useTheme() const { resolvedTheme, setTheme } = useTheme();
const toggleColorMode = () => { const toggleColorMode = () => {
setTheme(resolvedTheme === "light" ? "dark" : "light") setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
} };
return { return {
colorMode: resolvedTheme as ColorMode, colorMode: resolvedTheme as ColorMode,
setColorMode: setTheme, setColorMode: setTheme,
toggleColorMode, toggleColorMode,
} };
} }
export function useColorModeValue<T>(light: T, dark: T) { export function useColorModeValue<T>(light: T, dark: T) {
const { colorMode } = useColorMode() const { colorMode } = useColorMode();
return colorMode === "dark" ? dark : light return colorMode === 'dark' ? dark : light;
} }
export function ColorModeIcon() { export function ColorModeIcon() {
const { colorMode } = useColorMode() const { colorMode } = useColorMode();
return colorMode === "dark" ? <LuMoon /> : <LuSun /> return colorMode === 'dark' ? <LuMoon /> : <LuSun />;
} }
interface ColorModeButtonProps extends Omit<IconButtonProps, "aria-label"> {} interface ColorModeButtonProps extends Omit<IconButtonProps, 'aria-label'> {}
export const ColorModeButton = React.forwardRef< export const ColorModeButton = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
ColorModeButtonProps ColorModeButtonProps
>(function ColorModeButton(props, ref) { >(function ColorModeButton(props, ref) {
const { toggleColorMode } = useColorMode() const { toggleColorMode } = useColorMode();
return ( return (
<ClientOnly fallback={<Skeleton boxSize="8" />}> <ClientOnly fallback={<Skeleton boxSize="8" />}>
<IconButton <IconButton
@ -63,13 +64,13 @@ export const ColorModeButton = React.forwardRef<
{...props} {...props}
css={{ css={{
_icon: { _icon: {
width: "5", width: '5',
height: "5", height: '5',
}, },
}} }}
> >
<ColorModeIcon /> <ColorModeIcon />
</IconButton> </IconButton>
</ClientOnly> </ClientOnly>
) );
}) });

View File

@ -1,11 +1,13 @@
import { Dialog as ChakraDialog, Portal } from "@chakra-ui/react" import * as React from 'react';
import { CloseButton } from "./close-button"
import * as React from "react" import { Dialog as ChakraDialog, Portal } from '@chakra-ui/react';
import { CloseButton } from './close-button';
interface DialogContentProps extends ChakraDialog.ContentProps { interface DialogContentProps extends ChakraDialog.ContentProps {
portalled?: boolean portalled?: boolean;
portalRef?: React.RefObject<HTMLElement> portalRef?: React.RefObject<HTMLElement>;
backdrop?: boolean backdrop?: boolean;
} }
export const DialogContent = React.forwardRef< export const DialogContent = React.forwardRef<
@ -18,7 +20,7 @@ export const DialogContent = React.forwardRef<
portalRef, portalRef,
backdrop = true, backdrop = true,
...rest ...rest
} = props } = props;
return ( return (
<Portal disabled={!portalled} container={portalRef}> <Portal disabled={!portalled} container={portalRef}>
@ -29,8 +31,8 @@ export const DialogContent = React.forwardRef<
</ChakraDialog.Content> </ChakraDialog.Content>
</ChakraDialog.Positioner> </ChakraDialog.Positioner>
</Portal> </Portal>
) );
}) });
export const DialogCloseTrigger = React.forwardRef< export const DialogCloseTrigger = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
@ -48,15 +50,15 @@ export const DialogCloseTrigger = React.forwardRef<
{props.children} {props.children}
</CloseButton> </CloseButton>
</ChakraDialog.CloseTrigger> </ChakraDialog.CloseTrigger>
) );
}) });
export const DialogRoot = ChakraDialog.Root export const DialogRoot = ChakraDialog.Root;
export const DialogFooter = ChakraDialog.Footer export const DialogFooter = ChakraDialog.Footer;
export const DialogHeader = ChakraDialog.Header export const DialogHeader = ChakraDialog.Header;
export const DialogBody = ChakraDialog.Body export const DialogBody = ChakraDialog.Body;
export const DialogBackdrop = ChakraDialog.Backdrop export const DialogBackdrop = ChakraDialog.Backdrop;
export const DialogTitle = ChakraDialog.Title export const DialogTitle = ChakraDialog.Title;
export const DialogDescription = ChakraDialog.Description export const DialogDescription = ChakraDialog.Description;
export const DialogTrigger = ChakraDialog.Trigger export const DialogTrigger = ChakraDialog.Trigger;
export const DialogActionTrigger = ChakraDialog.ActionTrigger export const DialogActionTrigger = ChakraDialog.ActionTrigger;

View File

@ -1,18 +1,20 @@
import { Drawer as ChakraDrawer, Portal } from "@chakra-ui/react" import * as React from 'react';
import { CloseButton } from "./close-button"
import * as React from "react" import { Drawer as ChakraDrawer, Portal } from '@chakra-ui/react';
import { CloseButton } from './close-button';
interface DrawerContentProps extends ChakraDrawer.ContentProps { interface DrawerContentProps extends ChakraDrawer.ContentProps {
portalled?: boolean portalled?: boolean;
portalRef?: React.RefObject<HTMLElement> portalRef?: React.RefObject<HTMLElement>;
offset?: ChakraDrawer.ContentProps["padding"] offset?: ChakraDrawer.ContentProps['padding'];
} }
export const DrawerContent = React.forwardRef< export const DrawerContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
DrawerContentProps DrawerContentProps
>(function DrawerContent(props, ref) { >(function DrawerContent(props, ref) {
const { children, portalled = true, portalRef, offset, ...rest } = props const { children, portalled = true, portalRef, offset, ...rest } = props;
return ( return (
<Portal disabled={!portalled} container={portalRef}> <Portal disabled={!portalled} container={portalRef}>
<ChakraDrawer.Positioner padding={offset}> <ChakraDrawer.Positioner padding={offset}>
@ -21,8 +23,8 @@ export const DrawerContent = React.forwardRef<
</ChakraDrawer.Content> </ChakraDrawer.Content>
</ChakraDrawer.Positioner> </ChakraDrawer.Positioner>
</Portal> </Portal>
) );
}) });
export const DrawerCloseTrigger = React.forwardRef< export const DrawerCloseTrigger = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
@ -38,15 +40,15 @@ export const DrawerCloseTrigger = React.forwardRef<
> >
<CloseButton size="sm" ref={ref} /> <CloseButton size="sm" ref={ref} />
</ChakraDrawer.CloseTrigger> </ChakraDrawer.CloseTrigger>
) );
}) });
export const DrawerTrigger = ChakraDrawer.Trigger export const DrawerTrigger = ChakraDrawer.Trigger;
export const DrawerRoot = ChakraDrawer.Root export const DrawerRoot = ChakraDrawer.Root;
export const DrawerFooter = ChakraDrawer.Footer export const DrawerFooter = ChakraDrawer.Footer;
export const DrawerHeader = ChakraDrawer.Header export const DrawerHeader = ChakraDrawer.Header;
export const DrawerBody = ChakraDrawer.Body export const DrawerBody = ChakraDrawer.Body;
export const DrawerBackdrop = ChakraDrawer.Backdrop export const DrawerBackdrop = ChakraDrawer.Backdrop;
export const DrawerDescription = ChakraDrawer.Description export const DrawerDescription = ChakraDrawer.Description;
export const DrawerTitle = ChakraDrawer.Title export const DrawerTitle = ChakraDrawer.Title;
export const DrawerActionTrigger = ChakraDrawer.ActionTrigger export const DrawerActionTrigger = ChakraDrawer.ActionTrigger;

View File

@ -1,17 +1,18 @@
import { Field as ChakraField } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
export interface FieldProps extends Omit<ChakraField.RootProps, "label"> { import { Field as ChakraField } from '@chakra-ui/react';
label?: React.ReactNode
helperText?: React.ReactNode export interface FieldProps extends Omit<ChakraField.RootProps, 'label'> {
errorText?: React.ReactNode label?: React.ReactNode;
optionalText?: React.ReactNode helperText?: React.ReactNode;
errorText?: React.ReactNode;
optionalText?: React.ReactNode;
} }
export const Field = React.forwardRef<HTMLDivElement, FieldProps>( export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
function Field(props, ref) { function Field(props, ref) {
const { label, children, helperText, errorText, optionalText, ...rest } = const { label, children, helperText, errorText, optionalText, ...rest } =
props props;
return ( return (
<ChakraField.Root ref={ref} {...rest}> <ChakraField.Root ref={ref} {...rest}>
{label && ( {label && (
@ -28,6 +29,6 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
<ChakraField.ErrorText>{errorText}</ChakraField.ErrorText> <ChakraField.ErrorText>{errorText}</ChakraField.ErrorText>
)} )}
</ChakraField.Root> </ChakraField.Root>
) );
}, }
) );

View File

@ -1,15 +1,16 @@
import type { BoxProps, InputElementProps } from "@chakra-ui/react" import * as React from 'react';
import { Group, InputElement } from "@chakra-ui/react"
import * as React from "react" import type { BoxProps, InputElementProps } from '@chakra-ui/react';
import { Group, InputElement } from '@chakra-ui/react';
export interface InputGroupProps extends BoxProps { export interface InputGroupProps extends BoxProps {
startElementProps?: InputElementProps startElementProps?: InputElementProps;
endElementProps?: InputElementProps endElementProps?: InputElementProps;
startElement?: React.ReactNode startElement?: React.ReactNode;
endElement?: React.ReactNode endElement?: React.ReactNode;
children: React.ReactElement<InputElementProps> children: React.ReactElement<InputElementProps>;
startOffset?: InputElementProps["paddingStart"] startOffset?: InputElementProps['paddingStart'];
endOffset?: InputElementProps["paddingEnd"] endOffset?: InputElementProps['paddingEnd'];
} }
export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>( export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
@ -20,13 +21,13 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
endElement, endElement,
endElementProps, endElementProps,
children, children,
startOffset = "6px", startOffset = '6px',
endOffset = "6px", endOffset = '6px',
...rest ...rest
} = props } = props;
const child = const child =
React.Children.only<React.ReactElement<InputElementProps>>(children) React.Children.only<React.ReactElement<InputElementProps>>(children);
return ( return (
<Group ref={ref} {...rest}> <Group ref={ref} {...rest}>
@ -48,6 +49,6 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
</InputElement> </InputElement>
)} )}
</Group> </Group>
) );
}, }
) );

View File

@ -1,26 +1,27 @@
"use client" 'use client';
import { AbsoluteCenter, Menu as ChakraMenu, Portal } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { LuCheck, LuChevronRight } from "react-icons/lu" import { AbsoluteCenter, Menu as ChakraMenu, Portal } from '@chakra-ui/react';
import { LuCheck, LuChevronRight } from 'react-icons/lu';
interface MenuContentProps extends ChakraMenu.ContentProps { interface MenuContentProps extends ChakraMenu.ContentProps {
portalled?: boolean portalled?: boolean;
portalRef?: React.RefObject<HTMLElement> portalRef?: React.RefObject<HTMLElement>;
} }
export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>( export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(
function MenuContent(props, ref) { function MenuContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props const { portalled = true, portalRef, ...rest } = props;
return ( return (
<Portal disabled={!portalled} container={portalRef}> <Portal disabled={!portalled} container={portalRef}>
<ChakraMenu.Positioner> <ChakraMenu.Positioner>
<ChakraMenu.Content ref={ref} {...rest} /> <ChakraMenu.Content ref={ref} {...rest} />
</ChakraMenu.Positioner> </ChakraMenu.Positioner>
</Portal> </Portal>
) );
}, }
) );
export const MenuArrow = React.forwardRef< export const MenuArrow = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -30,8 +31,8 @@ export const MenuArrow = React.forwardRef<
<ChakraMenu.Arrow ref={ref} {...props}> <ChakraMenu.Arrow ref={ref} {...props}>
<ChakraMenu.ArrowTip /> <ChakraMenu.ArrowTip />
</ChakraMenu.Arrow> </ChakraMenu.Arrow>
) );
}) });
export const MenuCheckboxItem = React.forwardRef< export const MenuCheckboxItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -44,14 +45,14 @@ export const MenuCheckboxItem = React.forwardRef<
</ChakraMenu.ItemIndicator> </ChakraMenu.ItemIndicator>
{props.children} {props.children}
</ChakraMenu.CheckboxItem> </ChakraMenu.CheckboxItem>
) );
}) });
export const MenuRadioItem = React.forwardRef< export const MenuRadioItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
ChakraMenu.RadioItemProps ChakraMenu.RadioItemProps
>(function MenuRadioItem(props, ref) { >(function MenuRadioItem(props, ref) {
const { children, ...rest } = props const { children, ...rest } = props;
return ( return (
<ChakraMenu.RadioItem ps="8" ref={ref} {...rest}> <ChakraMenu.RadioItem ps="8" ref={ref} {...rest}>
<AbsoluteCenter axis="horizontal" left="4" asChild> <AbsoluteCenter axis="horizontal" left="4" asChild>
@ -61,14 +62,14 @@ export const MenuRadioItem = React.forwardRef<
</AbsoluteCenter> </AbsoluteCenter>
<ChakraMenu.ItemText>{children}</ChakraMenu.ItemText> <ChakraMenu.ItemText>{children}</ChakraMenu.ItemText>
</ChakraMenu.RadioItem> </ChakraMenu.RadioItem>
) );
}) });
export const MenuItemGroup = React.forwardRef< export const MenuItemGroup = React.forwardRef<
HTMLDivElement, HTMLDivElement,
ChakraMenu.ItemGroupProps ChakraMenu.ItemGroupProps
>(function MenuItemGroup(props, ref) { >(function MenuItemGroup(props, ref) {
const { title, children, ...rest } = props const { title, children, ...rest } = props;
return ( return (
<ChakraMenu.ItemGroup ref={ref} {...rest}> <ChakraMenu.ItemGroup ref={ref} {...rest}>
{title && ( {title && (
@ -78,33 +79,33 @@ export const MenuItemGroup = React.forwardRef<
)} )}
{children} {children}
</ChakraMenu.ItemGroup> </ChakraMenu.ItemGroup>
) );
}) });
export interface MenuTriggerItemProps extends ChakraMenu.ItemProps { export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
startIcon?: React.ReactNode startIcon?: React.ReactNode;
} }
export const MenuTriggerItem = React.forwardRef< export const MenuTriggerItem = React.forwardRef<
HTMLDivElement, HTMLDivElement,
MenuTriggerItemProps MenuTriggerItemProps
>(function MenuTriggerItem(props, ref) { >(function MenuTriggerItem(props, ref) {
const { startIcon, children, ...rest } = props const { startIcon, children, ...rest } = props;
return ( return (
<ChakraMenu.TriggerItem ref={ref} {...rest}> <ChakraMenu.TriggerItem ref={ref} {...rest}>
{startIcon} {startIcon}
{children} {children}
<LuChevronRight /> <LuChevronRight />
</ChakraMenu.TriggerItem> </ChakraMenu.TriggerItem>
) );
}) });
export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup;
export const MenuContextTrigger = ChakraMenu.ContextTrigger export const MenuContextTrigger = ChakraMenu.ContextTrigger;
export const MenuRoot = ChakraMenu.Root export const MenuRoot = ChakraMenu.Root;
export const MenuSeparator = ChakraMenu.Separator export const MenuSeparator = ChakraMenu.Separator;
export const MenuItem = ChakraMenu.Item export const MenuItem = ChakraMenu.Item;
export const MenuItemText = ChakraMenu.ItemText export const MenuItemText = ChakraMenu.ItemText;
export const MenuItemCommand = ChakraMenu.ItemCommand export const MenuItemCommand = ChakraMenu.ItemCommand;
export const MenuTrigger = ChakraMenu.Trigger export const MenuTrigger = ChakraMenu.Trigger;

View File

@ -1,5 +1,6 @@
import { NumberInput as ChakraNumberInput } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { NumberInput as ChakraNumberInput } from '@chakra-ui/react';
export interface NumberInputProps extends ChakraNumberInput.RootProps {} export interface NumberInputProps extends ChakraNumberInput.RootProps {}
@ -7,7 +8,7 @@ export const NumberInputRoot = React.forwardRef<
HTMLDivElement, HTMLDivElement,
NumberInputProps NumberInputProps
>(function NumberInput(props, ref) { >(function NumberInput(props, ref) {
const { children, ...rest } = props const { children, ...rest } = props;
return ( return (
<ChakraNumberInput.Root ref={ref} variant="outline" {...rest}> <ChakraNumberInput.Root ref={ref} variant="outline" {...rest}>
{children} {children}
@ -16,9 +17,9 @@ export const NumberInputRoot = React.forwardRef<
<ChakraNumberInput.DecrementTrigger /> <ChakraNumberInput.DecrementTrigger />
</ChakraNumberInput.Control> </ChakraNumberInput.Control>
</ChakraNumberInput.Root> </ChakraNumberInput.Root>
) );
}) });
export const NumberInputField = ChakraNumberInput.Input export const NumberInputField = ChakraNumberInput.Input;
export const NumberInputScrubber = ChakraNumberInput.Scrubber export const NumberInputScrubber = ChakraNumberInput.Scrubber;
export const NumberInputLabel = ChakraNumberInput.Label export const NumberInputLabel = ChakraNumberInput.Label;

View File

@ -1,25 +1,27 @@
import { Popover as ChakraPopover, Portal } from "@chakra-ui/react" import * as React from 'react';
import { CloseButton } from "./close-button"
import * as React from "react" import { Popover as ChakraPopover, Portal } from '@chakra-ui/react';
import { CloseButton } from './close-button';
interface PopoverContentProps extends ChakraPopover.ContentProps { interface PopoverContentProps extends ChakraPopover.ContentProps {
portalled?: boolean portalled?: boolean;
portalRef?: React.RefObject<HTMLElement> portalRef?: React.RefObject<HTMLElement>;
} }
export const PopoverContent = React.forwardRef< export const PopoverContent = React.forwardRef<
HTMLDivElement, HTMLDivElement,
PopoverContentProps PopoverContentProps
>(function PopoverContent(props, ref) { >(function PopoverContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props const { portalled = true, portalRef, ...rest } = props;
return ( return (
<Portal disabled={!portalled} container={portalRef}> <Portal disabled={!portalled} container={portalRef}>
<ChakraPopover.Positioner> <ChakraPopover.Positioner>
<ChakraPopover.Content ref={ref} {...rest} /> <ChakraPopover.Content ref={ref} {...rest} />
</ChakraPopover.Positioner> </ChakraPopover.Positioner>
</Portal> </Portal>
) );
}) });
export const PopoverArrow = React.forwardRef< export const PopoverArrow = React.forwardRef<
HTMLDivElement, HTMLDivElement,
@ -29,8 +31,8 @@ export const PopoverArrow = React.forwardRef<
<ChakraPopover.Arrow {...props} ref={ref}> <ChakraPopover.Arrow {...props} ref={ref}>
<ChakraPopover.ArrowTip /> <ChakraPopover.ArrowTip />
</ChakraPopover.Arrow> </ChakraPopover.Arrow>
) );
}) });
export const PopoverCloseTrigger = React.forwardRef< export const PopoverCloseTrigger = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
@ -47,13 +49,13 @@ export const PopoverCloseTrigger = React.forwardRef<
> >
<CloseButton size="sm" /> <CloseButton size="sm" />
</ChakraPopover.CloseTrigger> </ChakraPopover.CloseTrigger>
) );
}) });
export const PopoverTitle = ChakraPopover.Title export const PopoverTitle = ChakraPopover.Title;
export const PopoverDescription = ChakraPopover.Description export const PopoverDescription = ChakraPopover.Description;
export const PopoverFooter = ChakraPopover.Footer export const PopoverFooter = ChakraPopover.Footer;
export const PopoverHeader = ChakraPopover.Header export const PopoverHeader = ChakraPopover.Header;
export const PopoverRoot = ChakraPopover.Root export const PopoverRoot = ChakraPopover.Root;
export const PopoverBody = ChakraPopover.Body export const PopoverBody = ChakraPopover.Body;
export const PopoverTrigger = ChakraPopover.Trigger export const PopoverTrigger = ChakraPopover.Trigger;

View File

@ -1,15 +1,13 @@
"use client" 'use client';
import { ChakraProvider, defaultSystem } from "@chakra-ui/react" import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
import {
ColorModeProvider, import { ColorModeProvider, type ColorModeProviderProps } from './color-mode';
type ColorModeProviderProps,
} from "./color-mode"
export function Provider(props: ColorModeProviderProps) { export function Provider(props: ColorModeProviderProps) {
return ( return (
<ChakraProvider value={defaultSystem}> <ChakraProvider value={defaultSystem}>
<ColorModeProvider {...props} /> <ColorModeProvider {...props} />
</ChakraProvider> </ChakraProvider>
) );
} }

View File

@ -1,14 +1,15 @@
import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { RadioGroup as ChakraRadioGroup } from '@chakra-ui/react';
export interface RadioProps extends ChakraRadioGroup.ItemProps { export interface RadioProps extends ChakraRadioGroup.ItemProps {
rootRef?: React.Ref<HTMLDivElement> rootRef?: React.Ref<HTMLDivElement>;
inputProps?: React.InputHTMLAttributes<HTMLInputElement> inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
} }
export const Radio = React.forwardRef<HTMLInputElement, RadioProps>( export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
function Radio(props, ref) { function Radio(props, ref) {
const { children, inputProps, rootRef, ...rest } = props const { children, inputProps, rootRef, ...rest } = props;
return ( return (
<ChakraRadioGroup.Item ref={rootRef} {...rest}> <ChakraRadioGroup.Item ref={rootRef} {...rest}>
<ChakraRadioGroup.ItemHiddenInput ref={ref} {...inputProps} /> <ChakraRadioGroup.ItemHiddenInput ref={ref} {...inputProps} />
@ -17,8 +18,8 @@ export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
<ChakraRadioGroup.ItemText>{children}</ChakraRadioGroup.ItemText> <ChakraRadioGroup.ItemText>{children}</ChakraRadioGroup.ItemText>
)} )}
</ChakraRadioGroup.Item> </ChakraRadioGroup.Item>
) );
}, }
) );
export const RadioGroup = ChakraRadioGroup.Root export const RadioGroup = ChakraRadioGroup.Root;

View File

@ -1,23 +1,24 @@
import { Slider as ChakraSlider, For, HStack } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { Slider as ChakraSlider, For, HStack } from '@chakra-ui/react';
export interface SliderProps extends ChakraSlider.RootProps { export interface SliderProps extends ChakraSlider.RootProps {
marks?: Array<number | { value: number; label: React.ReactNode }> marks?: Array<number | { value: number; label: React.ReactNode }>;
label?: React.ReactNode label?: React.ReactNode;
showValue?: boolean showValue?: boolean;
} }
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>( export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
function Slider(props, ref) { function Slider(props, ref) {
const { marks: marksProp, label, showValue, ...rest } = props const { marks: marksProp, label, showValue, ...rest } = props;
const value = props.defaultValue ?? props.value const value = props.defaultValue ?? props.value;
const marks = marksProp?.map((mark) => { const marks = marksProp?.map((mark) => {
if (typeof mark === "number") return { value: mark, label: undefined } if (typeof mark === 'number') return { value: mark, label: undefined };
return mark return mark;
}) });
const hasMarkLabel = !!marks?.some((mark) => mark.label) const hasMarkLabel = !!marks?.some((mark) => mark.label);
return ( return (
<ChakraSlider.Root ref={ref} thumbAlignment="center" {...rest}> <ChakraSlider.Root ref={ref} thumbAlignment="center" {...rest}>
@ -38,12 +39,12 @@ export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
<SliderMarks marks={marks} /> <SliderMarks marks={marks} />
</ChakraSlider.Control> </ChakraSlider.Control>
</ChakraSlider.Root> </ChakraSlider.Root>
) );
}, }
) );
function SliderThumbs(props: { value?: number[] }) { function SliderThumbs(props: { value?: number[] }) {
const { value } = props const { value } = props;
return ( return (
<For each={value}> <For each={value}>
{(_, index) => ( {(_, index) => (
@ -52,31 +53,31 @@ function SliderThumbs(props: { value?: number[] }) {
</ChakraSlider.Thumb> </ChakraSlider.Thumb>
)} )}
</For> </For>
) );
} }
interface SliderMarksProps { interface SliderMarksProps {
marks?: Array<number | { value: number; label: React.ReactNode }> marks?: Array<number | { value: number; label: React.ReactNode }>;
} }
const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>( const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>(
function SliderMarks(props, ref) { function SliderMarks(props, ref) {
const { marks } = props const { marks } = props;
if (!marks?.length) return null if (!marks?.length) return null;
return ( return (
<ChakraSlider.MarkerGroup ref={ref}> <ChakraSlider.MarkerGroup ref={ref}>
{marks.map((mark, index) => { {marks.map((mark, index) => {
const value = typeof mark === "number" ? mark : mark.value const value = typeof mark === 'number' ? mark : mark.value;
const label = typeof mark === "number" ? undefined : mark.label const label = typeof mark === 'number' ? undefined : mark.label;
return ( return (
<ChakraSlider.Marker key={index} value={value}> <ChakraSlider.Marker key={index} value={value}>
<ChakraSlider.MarkerIndicator /> <ChakraSlider.MarkerIndicator />
{label} {label}
</ChakraSlider.Marker> </ChakraSlider.Marker>
) );
})} })}
</ChakraSlider.MarkerGroup> </ChakraSlider.MarkerGroup>
) );
}, }
) );

View File

@ -1,6 +1,5 @@
"use client" 'use client';
import { RestErrorResponse } from "@/back-api";
import { import {
Toaster as ChakraToaster, Toaster as ChakraToaster,
Portal, Portal,
@ -8,12 +7,14 @@ import {
Stack, Stack,
Toast, Toast,
createToaster, createToaster,
} from "@chakra-ui/react" } from '@chakra-ui/react';
import { RestErrorResponse } from '@/back-api';
export const toaster = createToaster({ export const toaster = createToaster({
placement: "bottom-end", placement: 'bottom-end',
pauseOnPageIdle: true, pauseOnPageIdle: true,
}) });
export const toasterAPIError = (error: RestErrorResponse) => { export const toasterAPIError = (error: RestErrorResponse) => {
toaster.create({ toaster.create({
@ -25,10 +26,10 @@ export const toasterAPIError = (error: RestErrorResponse) => {
export const Toaster = () => { export const Toaster = () => {
return ( return (
<Portal> <Portal>
<ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}> <ChakraToaster toaster={toaster} insetInline={{ mdDown: '4' }}>
{(toast) => ( {(toast) => (
<Toast.Root width={{ md: "sm" }}> <Toast.Root width={{ md: 'sm' }}>
{toast.type === "loading" ? ( {toast.type === 'loading' ? (
<Spinner size="sm" color="blue.solid" /> <Spinner size="sm" color="blue.solid" />
) : ( ) : (
<Toast.Indicator /> <Toast.Indicator />
@ -47,5 +48,5 @@ export const Toaster = () => {
)} )}
</ChakraToaster> </ChakraToaster>
</Portal> </Portal>
) );
} };

View File

@ -1,13 +1,14 @@
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react" import * as React from 'react';
import * as React from "react"
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
export interface TooltipProps extends ChakraTooltip.RootProps { export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean showArrow?: boolean;
portalled?: boolean portalled?: boolean;
portalRef?: React.RefObject<HTMLElement> portalRef?: React.RefObject<HTMLElement>;
content: React.ReactNode content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean disabled?: boolean;
} }
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
@ -21,9 +22,9 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
contentProps, contentProps,
portalRef, portalRef,
...rest ...rest
} = props } = props;
if (disabled) return children if (disabled) return children;
return ( return (
<ChakraTooltip.Root {...rest}> <ChakraTooltip.Root {...rest}>
@ -41,6 +42,6 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
</ChakraTooltip.Positioner> </ChakraTooltip.Positioner>
</Portal> </Portal>
</ChakraTooltip.Root> </ChakraTooltip.Root>
) );
}, }
) );

View File

@ -1,28 +0,0 @@
import dayjs from 'dayjs';
import 'dayjs/locale/fr';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import dayOfYear from 'dayjs/plugin/dayOfYear';
import duration from 'dayjs/plugin/duration';
import isBetween from 'dayjs/plugin/isBetween';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import isToday from 'dayjs/plugin/isToday';
import isTomorrow from 'dayjs/plugin/isTomorrow';
import isYesterday from 'dayjs/plugin/isYesterday';
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
import relativeTime from 'dayjs/plugin/relativeTime';
import weekOfYear from 'dayjs/plugin/weekOfYear';
dayjs.locale('fr');
dayjs.extend(relativeTime);
dayjs.extend(customParseFormat);
dayjs.extend(weekOfYear);
dayjs.extend(isSameOrAfter);
dayjs.extend(isToday);
dayjs.extend(isTomorrow);
dayjs.extend(isYesterday);
dayjs.extend(dayOfYear);
dayjs.extend(isBetween);
dayjs.extend(advancedFormat);
dayjs.extend(quarterOfYear);
dayjs.extend(duration);

View File

@ -1,2 +0,0 @@
import './axios';
import './dayjs';

View File

@ -1,2 +0,0 @@
export const DATE_FORMAT = 'YYYY-MM-DD';
export const DATE_FORMAT_FULL = 'dddd DD MMMM HH:mm';

View File

@ -1,4 +0,0 @@
export const BASE_WRAP_SPACING = { base: "5px", md: "10px", lg: "20px" };
export const BASE_WRAP_WIDTH = { base: "90%", md: "45%", lg: "270px" };
export const BASE_WRAP_HEIGHT = { base: "75px", lg: "120px" };
export const BASE_WRAP_ICON_SIZE = { base: "50px", lg: "100px" };

View File

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

View File

@ -42,8 +42,9 @@ export const isDevelopmentEnvironment = () => {
return import.meta.env.MODE === 'development'; return import.meta.env.MODE === 'development';
}; };
export const environment = isDevelopmentEnvironment() ? environment_local : environment_back_prod; export const environment = isDevelopmentEnvironment()
? environment_local
: environment_back_prod;
/** /**
* get the current REST api URL. Depend on the VITE_API_BASE_URL env variable. * get the current REST api URL. Depend on the VITE_API_BASE_URL env variable.

View File

@ -1,4 +1,4 @@
import { Box, Button, Center, Heading, Link, Text } from '@chakra-ui/react'; import { Box, Center, Heading, Link, Text } from '@chakra-ui/react';
import { MdControlCamera } from 'react-icons/md'; import { MdControlCamera } from 'react-icons/md';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter'; import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
@ -10,15 +10,17 @@ export const Error401 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdControlCamera style={{ width: "250px", height: "250px", color: "orange" }} /> <MdControlCamera
style={{ width: '250px', height: '250px', color: 'orange' }}
/>
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 401</Heading> <Heading>Error 401</Heading>
<Text color="red.600"> <Text color="red.600">
Vous n'êtes pas autorisé a accéder a ce contenu. You are not authorized to access this content.
</Text> </Text>
<Link as="a" href="/"> <Link as="a" href="/">
Retour à l'accueil Back to Homepage
</Link> </Link>
</Box> </Box>
</PageLayoutInfoCenter> </PageLayoutInfoCenter>

View File

@ -1,4 +1,4 @@
import { Box, Button, Center, Heading, Link, Text } from '@chakra-ui/react'; import { Box, Center, Heading, Link, Text } from '@chakra-ui/react';
import { MdDangerous } from 'react-icons/md'; import { MdDangerous } from 'react-icons/md';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter'; import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
@ -10,14 +10,14 @@ export const Error403 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdDangerous style={{ width: "250px", height: "250px", color: "red" }} /> <MdDangerous
style={{ width: '250px', height: '250px', color: 'red' }}
/>
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 403</Heading> <Heading>Error 403</Heading>
<Text color="orange.600">Cette page vous est interdite</Text> <Text color="orange.600">This page is forbidden to you.</Text>
<Link href="/"> <Link href="/">Back to Homepage</Link>
Retour à l'accueil
</Link>
</Box> </Box>
</PageLayoutInfoCenter> </PageLayoutInfoCenter>
</> </>

View File

@ -1,4 +1,4 @@
import { Box, Button, Center, Heading, Link, Text } from '@chakra-ui/react'; import { Box, Center, Heading, Link, Text } from '@chakra-ui/react';
import { MdSignpost } from 'react-icons/md'; import { MdSignpost } from 'react-icons/md';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter'; import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
@ -10,16 +10,16 @@ export const Error404 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdSignpost style={{ width: "250px", height: "250px", color: "aqua" }} /> <MdSignpost
style={{ width: '250px', height: '250px', color: 'aqua' }}
/>
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 404</Heading> <Heading>Error 404</Heading>
<Text color="gray.600"> <Text color="gray.600">
Cette page n'existe plus ou l'URL a changé This page no longer exists or the URL has changed.
</Text> </Text>
<Link href="/"> <Link href="/">Back to Homepage</Link>
Retour à l'accueil
</Link>
</Box> </Box>
</PageLayoutInfoCenter> </PageLayoutInfoCenter>
</> </>

View File

@ -1,11 +1,6 @@
import React, { FC } from 'react'; import { ReactNode } from 'react';
import { import { Alert, AlertDescription, AlertTitle, Box } from '@chakra-ui/react';
AlertDescription,
AlertTitle,
Box,
Alert,
} from '@chakra-ui/react';
import { import {
FallbackProps, FallbackProps,
ErrorBoundary as ReactErrorBoundary, ErrorBoundary as ReactErrorBoundary,
@ -17,8 +12,15 @@ const ErrorFallback = ({ error }: FallbackProps) => {
<Alert.Root status="error" borderRadius="md"> <Alert.Root status="error" borderRadius="md">
<Alert.Indicator height="75px" width="75px" /> <Alert.Indicator height="75px" width="75px" />
<Box flex="1"> <Box flex="1">
<AlertTitle fontWeight="bold" fontSize="35px">An unexpected error has occurred.</AlertTitle> <AlertTitle fontWeight="bold" fontSize="35px">
<AlertDescription padding="5" marginTop="3" fontSize="20px" lineHeight="1.4"> An unexpected error has occurred.
</AlertTitle>
<AlertDescription
padding="5"
marginTop="3"
fontSize="20px"
lineHeight="1.4"
>
<br /> <br />
{error.message} {error.message}
</AlertDescription> </AlertDescription>
@ -28,6 +30,6 @@ const ErrorFallback = ({ error }: FallbackProps) => {
); );
}; };
export const ErrorBoundary: FC<React.PropsWithChildren<unknown>> = (props) => { export const ErrorBoundary = ({ children }: { children: ReactNode }) => {
return <ReactErrorBoundary FallbackComponent={ErrorFallback} {...props} />; return <ReactErrorBoundary FallbackComponent={ErrorFallback} {...props} />;
}; };

View File

@ -1,13 +1,13 @@
import { StrictMode } from 'react'; import { StrictMode } from 'react';
import { ChakraProvider } from '@chakra-ui/react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import App from '@/App'; import App from '@/App';
import { ColorModeProvider } from './components/ui/color-mode';
import { ChakraProvider } from '@chakra-ui/react'; import { ColorModeProvider } from './components/ui/color-mode';
import { systemTheme } from './theme/theme';
import { Toaster } from './components/ui/toaster'; import { Toaster } from './components/ui/toaster';
import { systemTheme } from './theme/theme';
// Render the app // Render the app
const rootElement = document.getElementById('root'); const rootElement = document.getElementById('root');
@ -15,12 +15,12 @@ if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement); const root = ReactDOM.createRoot(rootElement);
root.render( root.render(
<StrictMode> <StrictMode>
<ColorModeProvider> <ColorModeProvider>
<ChakraProvider value={systemTheme}> <ChakraProvider value={systemTheme}>
<App /> <App />
<Toaster /> <Toaster />
</ChakraProvider> </ChakraProvider>
</ColorModeProvider> </ColorModeProvider>
</StrictMode> </StrictMode>
); );
} }

View File

@ -9,11 +9,12 @@ import { Error404 } from '@/errors';
import { HelpPage } from '@/scene/home/HelpPage'; import { HelpPage } from '@/scene/home/HelpPage';
import { HomePage } from '@/scene/home/HomePage'; import { HomePage } from '@/scene/home/HomePage';
import { useHasRight } from '@/service/session'; import { useHasRight } from '@/service/session';
import { SettingsPage } from './home/SettingsPage';
import { ManageAccountPage } from './account/ManageAccountPage';
import { SignInDonePage } from './connection/SignInDonePage';
import { SignInPage } from './connection/SignInPage'; import { SignInPage } from './connection/SignInPage';
import { SignOutPage } from './connection/SignOutPage'; import { SignOutPage } from './connection/SignOutPage';
import { SignInDonePage } from './connection/SignInDonePage'; import { SettingsPage } from './home/SettingsPage';
import { ManageAccountPage } from './account/ManageAccountPage';
export const AppRoutes = () => { export const AppRoutes = () => {
const { isReadable } = useHasRight('USER'); const { isReadable } = useHasRight('USER');
@ -28,8 +29,14 @@ export const AppRoutes = () => {
<> <>
<Route path="/" element={<HomePage />} /> <Route path="/" element={<HomePage />} />
{/* Connection page after SSO */} {/* Connection page after SSO */}
<Route path="signin/:applicationName/:applicationData/*" element={<SignInDonePage />} /> <Route
<Route path="signin/:applicationName/*" element={<SignInDonePage />} /> path="signin/:applicationName/:applicationData/*"
element={<SignInDonePage />}
/>
<Route
path="signin/:applicationName/*"
element={<SignInDonePage />}
/>
<Route path="signout/*" element={<SignOutPage />} /> <Route path="signout/*" element={<SignOutPage />} />
<Route path="manage-account/*" element={<ManageAccountPage />} /> <Route path="manage-account/*" element={<ManageAccountPage />} />
<Route path="help" element={<HelpPage />} /> <Route path="help" element={<HelpPage />} />
@ -39,7 +46,7 @@ export const AppRoutes = () => {
</> </>
) : ( ) : (
<> <>
<Route path="*" element={<SignInPage />} /> <Route path="*" element={<SignInPage />} />
</> </>
)} )}
</Routes> </Routes>

View File

@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { useEffect} from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useSessionService } from '@/service/session'; import { useSessionService } from '@/service/session';
export const SignOutPage = () => { export const SignOutPage = () => {

View File

@ -1,60 +1,60 @@
import { useState } from 'react'; import { useState } from 'react';
import { UserResource } from '@/back-api';
import { toaster, toasterAPIError } from '@/components/ui/toaster';
import { useSessionService } from '@/service/session';
//import { useSessionContext } from '@/context/SessionContext'; //import { useSessionContext } from '@/context/SessionContext';
import { getRestConfig } from '@/utils/http'; import { getRestConfig } from '@/utils/http';
import { sha512 } from '@/utils/sha512'; import { sha512 } from '@/utils/sha512';
import { toaster, toasterAPIError } from '@/components/ui/toaster';
import { useSessionService } from '@/service/session';
import { UserResource } from '@/back-api';
export const useLogin = () => { export const useLogin = () => {
const [isConnectionLoading, setIsConnectionLoading] = const [isConnectionLoading, setIsConnectionLoading] =
useState<boolean>(false); useState<boolean>(false);
const [lastError, setLastError] = useState<string>(''); const [lastError, setLastError] = useState<string>('');
const { setToken, clearToken } = useSessionService(); const { setToken, clearToken } = useSessionService();
/** /**
* Log-in with a clear password * Log-in with a clear password
* @param login The user's login identifier (username or email address) * @param login The user's login identifier (username or email address)
* @param password The user's plain-text password * @param password The user's plain-text password
* @returns promise on connection * @returns promise on connection
*/ */
const connect = (login: string, password: string) => { const connect = (login: string, password: string) => {
console.log(`call connect`); console.log(`call connect`);
clearToken(); clearToken();
setIsConnectionLoading(true); setIsConnectionLoading(true);
setLastError(''); setLastError('');
const currentDate: string = new Date().toISOString(); const currentDate: string = new Date().toISOString();
const data = { const data = {
login, login,
method: 'v1', method: 'v1',
time: currentDate, time: currentDate,
// we mix the password to be sure that it can not be used an other time ... // we mix the password to be sure that it can not be used an other time ...
password: sha512( password: sha512(
`login='${login}';pass='${sha512(password)}';date='${currentDate}'` `login='${login}';pass='${sha512(password)}';date='${currentDate}'`
), ),
};
console.log(`call backend : ${JSON.stringify(data, null, 2)}`);
UserResource.getToken({
restConfig: getRestConfig(),
data,
})
.then((response) => {
setToken(response.jwt);
toaster.create({
title: 'Connection done',
description: `Welcome, ${login}!`,
});
setIsConnectionLoading(false);
})
.catch(() => {
toaster.create({
title: 'Connection fail',
description: `Fail to connect with identification: '${login}'`,
});
setLastError('The connection Fail');
setIsConnectionLoading(false);
});
}; };
return { connect, lastError, isConnectionLoading }; console.log(`call backend : ${JSON.stringify(data, null, 2)}`);
}; UserResource.getToken({
restConfig: getRestConfig(),
data,
})
.then((response) => {
setToken(response.jwt);
toaster.create({
title: 'Connection done',
description: `Welcome, ${login}!`,
});
setIsConnectionLoading(false);
})
.catch(() => {
toaster.create({
title: 'Connection fail',
description: `Fail to connect with identification: '${login}'`,
});
setLastError('The connection Fail');
setIsConnectionLoading(false);
});
};
return { connect, lastError, isConnectionLoading };
};

View File

@ -5,9 +5,7 @@ export const HelpPage = () => {
return ( return (
<> <>
<TopBar title="Help" /> <TopBar title="Help" />
<PageLayout> <PageLayout>No help available right now</PageLayout>
No help available right now
</PageLayout>
</> </>
); );
}; };

View File

@ -1,14 +1,11 @@
import { PageLayout } from '@/components/Layout/PageLayout'; import { PageLayout } from '@/components/Layout/PageLayout';
import { TopBar } from '@/components/TopBar/TopBar'; import { TopBar } from '@/components/TopBar/TopBar';
export const SettingsPage = () => { export const SettingsPage = () => {
return ( return (
<> <>
<TopBar title="Help" /> <TopBar title="Help" />
<PageLayout> <PageLayout>No settings available right now</PageLayout>
No settings available right now
</PageLayout>
</> </>
); );
}; };

View File

@ -1,11 +1,15 @@
import { ApplicationToken, ApplicationTokenResource, CreateTokenRequestWrite, Integer, Long, RestErrorResponse } from '@/back-api'; import {
ApplicationToken,
ApplicationTokenResource,
CreateTokenRequestWrite,
Integer,
Long,
RestErrorResponse,
} from '@/back-api';
import { useSessionService } from '@/service/session'; import { useSessionService } from '@/service/session';
import { useQuery, useQueryCall, useQueryCallProps } from '@/utils/query'; import { useQuery, useQueryCall, useQueryCallProps } from '@/utils/query';
export namespace ApplicationTokenService { export namespace ApplicationTokenService {
export const useApplicationTokens = (applicationId: Long) => { export const useApplicationTokens = (applicationId: Long) => {
const { getRestConfig } = useSessionService(); const { getRestConfig } = useSessionService();
return useQuery({ return useQuery({
@ -13,7 +17,7 @@ export namespace ApplicationTokenService {
return ApplicationTokenResource.gets({ return ApplicationTokenResource.gets({
restConfig: getRestConfig(), restConfig: getRestConfig(),
params: { params: {
applicationId applicationId,
}, },
}); });
}, },
@ -22,10 +26,15 @@ export namespace ApplicationTokenService {
export const useApplicationTokenCreate = ( export const useApplicationTokenCreate = (
applicationId: Long, applicationId: Long,
config: { config?: Omit<useQueryCallProps<ApplicationToken, any>, 'queryFunction'> } config: {
config?: Omit<useQueryCallProps<ApplicationToken, any>, 'queryFunction'>;
}
) => { ) => {
const { getRestConfig } = useSessionService(); const { getRestConfig } = useSessionService();
const { call, ...rest } = useQueryCall<ApplicationToken, CreateTokenRequestWrite>({ const { call, ...rest } = useQueryCall<
ApplicationToken,
CreateTokenRequestWrite
>({
queryFunction: (inputData) => { queryFunction: (inputData) => {
return ApplicationTokenResource.create({ return ApplicationTokenResource.create({
restConfig: getRestConfig(), restConfig: getRestConfig(),
@ -43,15 +52,18 @@ export namespace ApplicationTokenService {
config: { config?: Omit<useQueryCallProps<void, any>, 'queryFunction'> } config: { config?: Omit<useQueryCallProps<void, any>, 'queryFunction'> }
) => { ) => {
const { getRestConfig } = useSessionService(); const { getRestConfig } = useSessionService();
const { call, ...rest } = useQueryCall<void, { const { call, ...rest } = useQueryCall<
tokenId: Integer, void,
}>({ {
tokenId: Integer;
}
>({
queryFunction: (inputData) => { queryFunction: (inputData) => {
return ApplicationTokenResource.remove({ return ApplicationTokenResource.remove({
restConfig: getRestConfig(), restConfig: getRestConfig(),
params: { params: {
applicationId, applicationId,
...inputData ...inputData,
}, },
}); });
}, },
@ -59,5 +71,4 @@ export namespace ApplicationTokenService {
}); });
return { removeApplicationToken: call, ...rest }; return { removeApplicationToken: call, ...rest };
}; };
}
}

View File

@ -11,13 +11,11 @@ export type ServiceContextType = {
session: SessionServiceProps; session: SessionServiceProps;
}; };
export const ServiceContext = createContext<ServiceContextType>({ export const ServiceContext = createContext<ServiceContextType>({
session: { session: {
isConnected: false, isConnected: false,
setToken: (_token: string) => { }, setToken: (_token: string) => {},
clearToken: () => { }, clearToken: () => {},
hasReadRight: (_part: RightGroup) => false, hasReadRight: (_part: RightGroup) => false,
hasWriteRight: (_part: RightGroup) => false, hasWriteRight: (_part: RightGroup) => false,
getRestConfig: getRestConfig, getRestConfig: getRestConfig,

View File

@ -22,7 +22,7 @@ export function getRestConfig(): RESTConfig {
export type SessionServiceProps = { export type SessionServiceProps = {
token?: string; token?: string;
isConnected: boolean, isConnected: boolean;
setToken: (token: string) => void; setToken: (token: string) => void;
clearToken: () => void; clearToken: () => void;
login?: string; login?: string;
@ -59,7 +59,6 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
localStorage.setItem(TOKEN_KEY, token); localStorage.setItem(TOKEN_KEY, token);
} }
} }
}, [localStorage, parseToken, token]); }, [localStorage, parseToken, token]);
const setTokenLocal = useCallback( const setTokenLocal = useCallback(
@ -71,7 +70,7 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
); );
const clearToken = useCallback(() => { const clearToken = useCallback(() => {
console.log("Clear Token"); console.log('Clear Token');
setToken(undefined); setToken(undefined);
updateRight(); updateRight();
}, [updateRight, setToken]); }, [updateRight, setToken]);
@ -85,7 +84,9 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
if (right === undefined) { if (right === undefined) {
return false; return false;
} }
return [PartRight.READ, PartRight.READ_WRITE].includes(right?.[group] ?? 0); return [PartRight.READ, PartRight.READ_WRITE].includes(
right?.[group] ?? 0
);
}, },
[config] [config]
); );
@ -99,7 +100,9 @@ export const useSessionServiceWrapped = (): SessionServiceProps => {
if (right === undefined) { if (right === undefined) {
return false; return false;
} }
return [PartRight.READ, PartRight.READ_WRITE].includes(right?.[group] ?? 0); return [PartRight.READ, PartRight.READ_WRITE].includes(
right?.[group] ?? 0
);
}, },
[config] [config]
); );

View File

@ -3,45 +3,52 @@
* @copyright 2018, Edouard DUPIN, all right reserved * @copyright 2018, Edouard DUPIN, all right reserved
* @license PROPRIETARY (see license file) * @license PROPRIETARY (see license file)
*/ */
import {
RESTConfig,
UserAuthGet,
UserCreateWrite,
UserResource,
} from '@/back-api';
import { useQuery, useQueryCall, useQueryCallProps } from '@/utils/query';
import { RESTConfig, UserAuthGet, UserCreateWrite, UserResource } from "@/back-api"; import { useSessionService } from './session';
import { useQuery, useQueryCall, useQueryCallProps } from "@/utils/query";
import { useSessionService } from "./session";
export namespace UserService { export namespace UserService {
// export const useGetData = <RETURN_TYPE>(queryFunction: (config: RESTConfig) => Promise<RETURN_TYPE>) => {
// const { getRestConfig } = useSessionService();
// return useQuery({ queryFunction: () => queryFunction(getRestConfig()) });
// };
// export const useGetData = <RETURN_TYPE>(queryFunction: (config: RESTConfig) => Promise<RETURN_TYPE>) => { // export const useGets2 = useGetData((restConfig) => {
// const { getRestConfig } = useSessionService(); // return UserResource.gets({
// return useQuery({ queryFunction: () => queryFunction(getRestConfig()) }); // restConfig
// }; // });
// });
// export const useGets2 = useGetData((restConfig) => { export const useGets = () => {
// return UserResource.gets({ const { getRestConfig } = useSessionService();
// restConfig return useQuery({
// }); queryFunction: () => {
// }); return UserResource.gets({
restConfig: getRestConfig(),
export const useGets = () => { });
const { getRestConfig } = useSessionService(); },
return useQuery({ });
queryFunction: () => { };
return UserResource.gets({ export const useCreate = ({
restConfig: getRestConfig() config,
}); }: {
}, config?: Omit<useQueryCallProps<UserAuthGet, any>, 'queryFunction'>;
}); }) => {
}; const { getRestConfig } = useSessionService();
export const useCreate = ({ config }: { config?: Omit<useQueryCallProps<UserAuthGet, any>, 'queryFunction'> }) => { return useQueryCall<UserAuthGet, UserCreateWrite>({
const { getRestConfig } = useSessionService(); queryFunction: (data) => {
return useQueryCall<UserAuthGet, UserCreateWrite>({ return UserResource.create({
queryFunction: (data) => { restConfig: getRestConfig(),
return UserResource.create({ data: { ...data },
restConfig: getRestConfig(), });
data: { ...data }, },
}); ...config,
}, });
...config };
});
};
} }

View File

@ -1,7 +1,6 @@
type PandaColorModel = { type PandaColorModel = {
value: string; value: string;
} };
type ThemeModel = { type ThemeModel = {
50: PandaColorModel; 50: PandaColorModel;
100: PandaColorModel; 100: PandaColorModel;

View File

@ -1,5 +1,11 @@
import { createSystem, defaultConfig, mergeConfigs, SystemConfig } from "@chakra-ui/react" import {
import { colors } from "./colors" SystemConfig,
createSystem,
defaultConfig,
mergeConfigs,
} from '@chakra-ui/react';
import { colors } from './colors';
const baseTheme: SystemConfig = { const baseTheme: SystemConfig = {
globalCss: { globalCss: {
@ -8,25 +14,23 @@ const baseTheme: SystemConfig = {
bg: { _light: 'back.50', _dark: 'back.700' }, bg: { _light: 'back.50', _dark: 'back.700' },
color: { _light: 'text.900', _dark: 'text.50' }, color: { _light: 'text.900', _dark: 'text.50' },
fontFamily: 'Roboto, Helvetica, Arial, "sans-serif"', fontFamily: 'Roboto, Helvetica, Arial, "sans-serif"',
userSelect: 'none', /* Prevents text selection */ userSelect: 'none' /* Prevents text selection */,
}, },
svg: { svg: {
width: "32px", width: '32px',
height: "32px", height: '32px',
aspectRatio: "square", aspectRatio: 'square',
} },
}, },
theme: { theme: {
slotRecipes: { slotRecipes: {
dialog: { dialog: {
slots: [ slots: ['header'],
"header"
],
base: { base: {
header: { header: {
fontWeight: "bold", fontWeight: 'bold',
fontSize: "2xl", fontSize: '2xl',
color: { _dark: "brand.400", _light: "brand.500" } color: { _dark: 'brand.400', _light: 'brand.500' },
}, },
}, },
}, },
@ -36,21 +40,27 @@ const baseTheme: SystemConfig = {
base: { base: {
borderRadius: 0, borderRadius: 0,
_hover: { _hover: {
boxShadow: { _light: "0 8px 20px #000000e6", _dark: "0 8px 20px #000000e6" } boxShadow: {
_light: '0 8px 20px #000000e6',
_dark: '0 8px 20px #000000e6',
},
}, },
transitionDuration: "slower" transitionDuration: 'slower',
} },
}, },
input: { input: {
base: { base: {
borderRadius: 0, borderRadius: 0,
_hover: { _hover: {
boxShadow: { _light: "0 2px 5px #000000e6", _dark: "0 2px 5px #000000e6" } boxShadow: {
_light: '0 2px 5px #000000e6',
_dark: '0 2px 5px #000000e6',
},
}, },
//borderColor: { _light: "gray.800", _dark: "gray.50" }, //borderColor: { _light: "gray.800", _dark: "gray.50" },
backgroundColor: { _light: "gray.200", _dark: "gray.700" }, backgroundColor: { _light: 'gray.200', _dark: 'gray.700' },
transitionDuration: "slower" transitionDuration: 'slower',
} },
}, },
}, },
tokens: { tokens: {
@ -62,23 +72,23 @@ const baseTheme: SystemConfig = {
}, },
semanticTokens: { semanticTokens: {
colors: { colors: {
"@danger": { '@danger': {
solid: { value: "{colors.danger.500}" }, solid: { value: '{colors.danger.500}' },
contrast: { value: "{colors.danger.100}" }, contrast: { value: '{colors.danger.100}' },
fg: { value: "{colors.danger.900}" }, fg: { value: '{colors.danger.900}' },
muted: { value: "{colors.danger.100}" }, muted: { value: '{colors.danger.100}' },
subtle: { value: "{colors.danger.200}" }, subtle: { value: '{colors.danger.200}' },
emphasized: { value: "{colors.danger.300}" }, emphasized: { value: '{colors.danger.300}' },
focusRing: { value: "{colors.danger.500}" }, focusRing: { value: '{colors.danger.500}' },
}, },
"brand": { brand: {
solid: { value: "{colors.brand.500}" }, solid: { value: '{colors.brand.500}' },
contrast: { value: "{colors.brand.100}" }, contrast: { value: '{colors.brand.100}' },
fg: { value: "{colors.brand.900}" }, fg: { value: '{colors.brand.900}' },
muted: { value: "{colors.brand.100}" }, muted: { value: '{colors.brand.100}' },
subtle: { value: "{colors.brand.200}" }, subtle: { value: '{colors.brand.200}' },
emphasized: { value: "{colors.brand.300}" }, emphasized: { value: '{colors.brand.300}' },
focusRing: { value: "{colors.brand.500}" }, focusRing: { value: '{colors.brand.500}' },
}, },
}, },
}, },

View File

@ -1,22 +1,19 @@
export function arrayUnique(array: any[]) { export function arrayUnique(array: any[]) {
var a = array.concat(); var a = array.concat();
for (var i = 0; i < a.length; ++i) { for (var i = 0; i < a.length; ++i) {
for (var j = i + 1; j < a.length; ++j) { for (var j = i + 1; j < a.length; ++j) {
if (a[i] === a[j]) if (a[i] === a[j]) a.splice(j--, 1);
a.splice(j--, 1);
}
} }
}
return a; return a;
} }
export function shuffleArray<T>(array: T[]): T[] { export function shuffleArray<T>(array: T[]): T[] {
const out = [...array]; const out = [...array];
for (let i = out.length - 1; i > 0; i--) { for (let i = out.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); const j = Math.floor(Math.random() * (i + 1));
[out[i], out[j]] = [out[j], out[i]]; [out[i], out[j]] = [out[j], out[i]];
} }
return out; return out;
} }

View File

@ -6,8 +6,8 @@
import { DependencyList, useCallback, useEffect, useState } from 'react'; import { DependencyList, useCallback, useEffect, useState } from 'react';
import { RestErrorResponse } from '@/back-api'; import { RestErrorResponse } from '@/back-api';
import { isNullOrUndefined } from '@/utils/validator';
import { toasterAPIError } from '@/components/ui/toaster'; import { toasterAPIError } from '@/components/ui/toaster';
import { isNullOrUndefined } from '@/utils/validator';
export type DataStoreType<TYPE> = { export type DataStoreType<TYPE> = {
isLoading: boolean; isLoading: boolean;

View File

@ -1,4 +1,4 @@
import { JwtToken, RESTConfig } from '@/back-api'; import { RESTConfig } from '@/back-api';
import { getApiUrl } from '@/environment'; import { getApiUrl } from '@/environment';
import { getUserToken } from '@/service/session'; import { getUserToken } from '@/service/session';
@ -7,4 +7,4 @@ export function getRestConfig(): RESTConfig {
server: getApiUrl(), server: getApiUrl(),
token: getUserToken() ?? '', token: getUserToken() ?? '',
}; };
} }

View File

@ -1,83 +1,88 @@
import { RestErrorResponse } from "@/back-api"; import { useEffect, useState } from 'react';
import { toasterAPIError, toaster } from "@/components/ui/toaster";
import { useSessionService } from "@/service/session"; import { RestErrorResponse } from '@/back-api';
import { useState, useEffect } from "react"; import { toaster, toasterAPIError } from '@/components/ui/toaster';
import { useSessionService } from '@/service/session';
export type useQueryProps<TYPE> = { export type useQueryProps<TYPE> = {
queryFunction: () => Promise<TYPE>; queryFunction: () => Promise<TYPE>;
onSuccess?: (data: TYPE) => Promise<TYPE>; onSuccess?: (data: TYPE) => Promise<TYPE>;
onFail?: (error: RestErrorResponse) => void; onFail?: (error: RestErrorResponse) => void;
} };
export const useQuery = <TYPE,>({ export const useQuery = <TYPE>({
queryFunction, queryFunction,
onSuccess, onSuccess,
onFail, onFail,
}: useQueryProps<TYPE>) => { }: useQueryProps<TYPE>) => {
const { clearToken } = useSessionService(); const { clearToken } = useSessionService();
const [data, setData] = useState<TYPE | undefined>(undefined); const [data, setData] = useState<TYPE | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<RestErrorResponse | undefined>(undefined); const [error, setError] = useState<RestErrorResponse | undefined>(undefined);
useEffect(() => { useEffect(() => {
queryFunction().then((received) => { queryFunction()
setData(received); .then((received) => {
setIsLoading(false); setData(received);
if (onSuccess) { setIsLoading(false);
onSuccess(received); if (onSuccess) {
} onSuccess(received);
}).catch((error) => { }
setError(error); })
setIsLoading(false); .catch((error) => {
if (onFail) { setError(error);
onFail(error); setIsLoading(false);
} if (onFail) {
toasterAPIError(error); onFail(error);
if (error.status == 401) { }
// Authentication error ==> auto_disconnect toasterAPIError(error);
clearToken(); if (error.status == 401) {
toaster.create({ // Authentication error ==> auto_disconnect
title: "Disconnected by server", clearToken();
description: "Token is no more available", toaster.create({
}); title: 'Disconnected by server',
} description: 'Token is no more available',
}); });
}, [setError, setIsLoading, setData, toasterAPIError, clearToken, toaster]) }
return { data, isLoading, error }; });
} }, [setError, setIsLoading, setData, toasterAPIError, clearToken, toaster]);
return { data, isLoading, error };
};
export type useQueryCallProps<RETURN_TYPE, PARAMETER_TYPE> = { export type useQueryCallProps<RETURN_TYPE, PARAMETER_TYPE> = {
queryFunction: (params: PARAMETER_TYPE) => Promise<RETURN_TYPE>; queryFunction: (params: PARAMETER_TYPE) => Promise<RETURN_TYPE>;
onSuccess?: (data: RETURN_TYPE) => Promise<RETURN_TYPE>; onSuccess?: (data: RETURN_TYPE) => Promise<RETURN_TYPE>;
onFail?: (error: RestErrorResponse) => void; onFail?: (error: RestErrorResponse) => void;
} };
export const useQueryCall = <TYPE, PARAMETERS>({ export const useQueryCall = <TYPE, PARAMETERS>({
queryFunction, queryFunction,
onSuccess, onSuccess,
onFail, onFail,
}: useQueryCallProps<TYPE, PARAMETERS>) => { }: useQueryCallProps<TYPE, PARAMETERS>) => {
const { clearToken } = useSessionService(); const { clearToken } = useSessionService();
const [isCalling, setIsCalling] = useState<boolean>(true); const [isCalling, setIsCalling] = useState<boolean>(true);
const [error, setError] = useState<RestErrorResponse | undefined>(undefined); const [error, setError] = useState<RestErrorResponse | undefined>(undefined);
const call = (params: PARAMETERS) => { const call = (params: PARAMETERS) => {
queryFunction(params).then((received) => { queryFunction(params)
setIsCalling(false); .then((received) => {
if (onSuccess) { setIsCalling(false);
onSuccess(received); if (onSuccess) {
} onSuccess(received);
}).catch((error) => { }
setIsCalling(false); })
if (onFail) { .catch((error) => {
onFail(error); setIsCalling(false);
} if (onFail) {
toasterAPIError(error); onFail(error);
setError(error); }
if (error.status == 401) { toasterAPIError(error);
// Authentication error ==> auto_disconnect setError(error);
clearToken(); if (error.status == 401) {
toaster.create({ // Authentication error ==> auto_disconnect
title: "Disconnected by server", clearToken();
description: "Token is no more available", toaster.create({
}); title: 'Disconnected by server',
} description: 'Token is no more available',
}); });
} }
return { call, isCalling, error }; });
} };
return { call, isCalling, error };
};

View File

@ -3,340 +3,368 @@
*/ */
export function sha512(str) { export function sha512(str) {
function int64(msint_32, lsint_32) { function int64(msint_32, lsint_32) {
this.highOrder = msint_32; this.highOrder = msint_32;
this.lowOrder = lsint_32; this.lowOrder = lsint_32;
} }
let H = [ let H = [
new int64(0x6a09e667, 0xf3bcc908), new int64(0x6a09e667, 0xf3bcc908),
new int64(0xbb67ae85, 0x84caa73b), new int64(0xbb67ae85, 0x84caa73b),
new int64(0x3c6ef372, 0xfe94f82b), new int64(0x3c6ef372, 0xfe94f82b),
new int64(0xa54ff53a, 0x5f1d36f1), new int64(0xa54ff53a, 0x5f1d36f1),
new int64(0x510e527f, 0xade682d1), new int64(0x510e527f, 0xade682d1),
new int64(0x9b05688c, 0x2b3e6c1f), new int64(0x9b05688c, 0x2b3e6c1f),
new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x1f83d9ab, 0xfb41bd6b),
new int64(0x5be0cd19, 0x137e2179), new int64(0x5be0cd19, 0x137e2179),
]; ];
let K = [ let K = [
new int64(0x428a2f98, 0xd728ae22), new int64(0x428a2f98, 0xd728ae22),
new int64(0x71374491, 0x23ef65cd), new int64(0x71374491, 0x23ef65cd),
new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xb5c0fbcf, 0xec4d3b2f),
new int64(0xe9b5dba5, 0x8189dbbc), new int64(0xe9b5dba5, 0x8189dbbc),
new int64(0x3956c25b, 0xf348b538), new int64(0x3956c25b, 0xf348b538),
new int64(0x59f111f1, 0xb605d019), new int64(0x59f111f1, 0xb605d019),
new int64(0x923f82a4, 0xaf194f9b), new int64(0x923f82a4, 0xaf194f9b),
new int64(0xab1c5ed5, 0xda6d8118), new int64(0xab1c5ed5, 0xda6d8118),
new int64(0xd807aa98, 0xa3030242), new int64(0xd807aa98, 0xa3030242),
new int64(0x12835b01, 0x45706fbe), new int64(0x12835b01, 0x45706fbe),
new int64(0x243185be, 0x4ee4b28c), new int64(0x243185be, 0x4ee4b28c),
new int64(0x550c7dc3, 0xd5ffb4e2), new int64(0x550c7dc3, 0xd5ffb4e2),
new int64(0x72be5d74, 0xf27b896f), new int64(0x72be5d74, 0xf27b896f),
new int64(0x80deb1fe, 0x3b1696b1), new int64(0x80deb1fe, 0x3b1696b1),
new int64(0x9bdc06a7, 0x25c71235), new int64(0x9bdc06a7, 0x25c71235),
new int64(0xc19bf174, 0xcf692694), new int64(0xc19bf174, 0xcf692694),
new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xe49b69c1, 0x9ef14ad2),
new int64(0xefbe4786, 0x384f25e3), new int64(0xefbe4786, 0x384f25e3),
new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x0fc19dc6, 0x8b8cd5b5),
new int64(0x240ca1cc, 0x77ac9c65), new int64(0x240ca1cc, 0x77ac9c65),
new int64(0x2de92c6f, 0x592b0275), new int64(0x2de92c6f, 0x592b0275),
new int64(0x4a7484aa, 0x6ea6e483), new int64(0x4a7484aa, 0x6ea6e483),
new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x5cb0a9dc, 0xbd41fbd4),
new int64(0x76f988da, 0x831153b5), new int64(0x76f988da, 0x831153b5),
new int64(0x983e5152, 0xee66dfab), new int64(0x983e5152, 0xee66dfab),
new int64(0xa831c66d, 0x2db43210), new int64(0xa831c66d, 0x2db43210),
new int64(0xb00327c8, 0x98fb213f), new int64(0xb00327c8, 0x98fb213f),
new int64(0xbf597fc7, 0xbeef0ee4), new int64(0xbf597fc7, 0xbeef0ee4),
new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xc6e00bf3, 0x3da88fc2),
new int64(0xd5a79147, 0x930aa725), new int64(0xd5a79147, 0x930aa725),
new int64(0x06ca6351, 0xe003826f), new int64(0x06ca6351, 0xe003826f),
new int64(0x14292967, 0x0a0e6e70), new int64(0x14292967, 0x0a0e6e70),
new int64(0x27b70a85, 0x46d22ffc), new int64(0x27b70a85, 0x46d22ffc),
new int64(0x2e1b2138, 0x5c26c926), new int64(0x2e1b2138, 0x5c26c926),
new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x4d2c6dfc, 0x5ac42aed),
new int64(0x53380d13, 0x9d95b3df), new int64(0x53380d13, 0x9d95b3df),
new int64(0x650a7354, 0x8baf63de), new int64(0x650a7354, 0x8baf63de),
new int64(0x766a0abb, 0x3c77b2a8), new int64(0x766a0abb, 0x3c77b2a8),
new int64(0x81c2c92e, 0x47edaee6), new int64(0x81c2c92e, 0x47edaee6),
new int64(0x92722c85, 0x1482353b), new int64(0x92722c85, 0x1482353b),
new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa2bfe8a1, 0x4cf10364),
new int64(0xa81a664b, 0xbc423001), new int64(0xa81a664b, 0xbc423001),
new int64(0xc24b8b70, 0xd0f89791), new int64(0xc24b8b70, 0xd0f89791),
new int64(0xc76c51a3, 0x0654be30), new int64(0xc76c51a3, 0x0654be30),
new int64(0xd192e819, 0xd6ef5218), new int64(0xd192e819, 0xd6ef5218),
new int64(0xd6990624, 0x5565a910), new int64(0xd6990624, 0x5565a910),
new int64(0xf40e3585, 0x5771202a), new int64(0xf40e3585, 0x5771202a),
new int64(0x106aa070, 0x32bbd1b8), new int64(0x106aa070, 0x32bbd1b8),
new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x19a4c116, 0xb8d2d0c8),
new int64(0x1e376c08, 0x5141ab53), new int64(0x1e376c08, 0x5141ab53),
new int64(0x2748774c, 0xdf8eeb99), new int64(0x2748774c, 0xdf8eeb99),
new int64(0x34b0bcb5, 0xe19b48a8), new int64(0x34b0bcb5, 0xe19b48a8),
new int64(0x391c0cb3, 0xc5c95a63), new int64(0x391c0cb3, 0xc5c95a63),
new int64(0x4ed8aa4a, 0xe3418acb), new int64(0x4ed8aa4a, 0xe3418acb),
new int64(0x5b9cca4f, 0x7763e373), new int64(0x5b9cca4f, 0x7763e373),
new int64(0x682e6ff3, 0xd6b2b8a3), new int64(0x682e6ff3, 0xd6b2b8a3),
new int64(0x748f82ee, 0x5defb2fc), new int64(0x748f82ee, 0x5defb2fc),
new int64(0x78a5636f, 0x43172f60), new int64(0x78a5636f, 0x43172f60),
new int64(0x84c87814, 0xa1f0ab72), new int64(0x84c87814, 0xa1f0ab72),
new int64(0x8cc70208, 0x1a6439ec), new int64(0x8cc70208, 0x1a6439ec),
new int64(0x90befffa, 0x23631e28), new int64(0x90befffa, 0x23631e28),
new int64(0xa4506ceb, 0xde82bde9), new int64(0xa4506ceb, 0xde82bde9),
new int64(0xbef9a3f7, 0xb2c67915), new int64(0xbef9a3f7, 0xb2c67915),
new int64(0xc67178f2, 0xe372532b), new int64(0xc67178f2, 0xe372532b),
new int64(0xca273ece, 0xea26619c), new int64(0xca273ece, 0xea26619c),
new int64(0xd186b8c7, 0x21c0c207), new int64(0xd186b8c7, 0x21c0c207),
new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xeada7dd6, 0xcde0eb1e),
new int64(0xf57d4f7f, 0xee6ed178), new int64(0xf57d4f7f, 0xee6ed178),
new int64(0x06f067aa, 0x72176fba), new int64(0x06f067aa, 0x72176fba),
new int64(0x0a637dc5, 0xa2c898a6), new int64(0x0a637dc5, 0xa2c898a6),
new int64(0x113f9804, 0xbef90dae), new int64(0x113f9804, 0xbef90dae),
new int64(0x1b710b35, 0x131c471b), new int64(0x1b710b35, 0x131c471b),
new int64(0x28db77f5, 0x23047d84), new int64(0x28db77f5, 0x23047d84),
new int64(0x32caab7b, 0x40c72493), new int64(0x32caab7b, 0x40c72493),
new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x3c9ebe0a, 0x15c9bebc),
new int64(0x431d67c4, 0x9c100d4c), new int64(0x431d67c4, 0x9c100d4c),
new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x4cc5d4be, 0xcb3e42b6),
new int64(0x597f299c, 0xfc657e2a), new int64(0x597f299c, 0xfc657e2a),
new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x5fcb6fab, 0x3ad6faec),
new int64(0x6c44198c, 0x4a475817), new int64(0x6c44198c, 0x4a475817),
]; ];
let W = new Array(64); let W = new Array(64);
let a, b, c, d, e, f, g, h, i, j; let a, b, c, d, e, f, g, h, i, j;
let T1, T2; let T1, T2;
let charsize = 8; let charsize = 8;
function utf8_encode(data: any) { function utf8_encode(data: any) {
return unescape(encodeURIComponent(data)); return unescape(encodeURIComponent(data));
} }
function str2binb(data: any) { function str2binb(data: any) {
let bin: any[] = []; let bin: any[] = [];
let mask = (1 << charsize) - 1; let mask = (1 << charsize) - 1;
let len = data.length * charsize; let len = data.length * charsize;
for (let i = 0; i < len; i += charsize) { for (let i = 0; i < len; i += charsize) {
bin[i >> 5] |= (data.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32)); bin[i >> 5] |=
} (data.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
}
return bin; return bin;
} }
function binb2hex(binarray: any) { function binb2hex(binarray: any) {
let hex_tab = '0123456789abcdef'; let hex_tab = '0123456789abcdef';
let data = ''; let data = '';
let length = binarray.length * 4; let length = binarray.length * 4;
let srcByte; let srcByte;
for (let i = 0; i < length; i += 1) { for (let i = 0; i < length; i += 1) {
srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8); srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
data += hex_tab.charAt((srcByte >> 4) & 0xf) + hex_tab.charAt(srcByte & 0xf); data +=
} hex_tab.charAt((srcByte >> 4) & 0xf) + hex_tab.charAt(srcByte & 0xf);
}
return data; return data;
} }
function safe_add_2(x: any, y: any) { function safe_add_2(x: any, y: any) {
let lsw, msw, lowOrder, highOrder; let lsw, msw, lowOrder, highOrder;
lsw = (x.lowOrder & 0xffff) + (y.lowOrder & 0xffff); lsw = (x.lowOrder & 0xffff) + (y.lowOrder & 0xffff);
msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
lsw = (x.highOrder & 0xffff) + (y.highOrder & 0xffff) + (msw >>> 16); lsw = (x.highOrder & 0xffff) + (y.highOrder & 0xffff) + (msw >>> 16);
msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
return new int64(highOrder, lowOrder); return new int64(highOrder, lowOrder);
} }
function safe_add_4(a, b, c, d) { function safe_add_4(a, b, c, d) {
let lsw, msw, lowOrder, highOrder; let lsw, msw, lowOrder, highOrder;
lsw = (a.lowOrder & 0xffff) + (b.lowOrder & 0xffff) + (c.lowOrder & 0xffff) + (d.lowOrder & 0xffff); lsw =
msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); (a.lowOrder & 0xffff) +
lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); (b.lowOrder & 0xffff) +
(c.lowOrder & 0xffff) +
(d.lowOrder & 0xffff);
msw =
(a.lowOrder >>> 16) +
(b.lowOrder >>> 16) +
(c.lowOrder >>> 16) +
(d.lowOrder >>> 16) +
(lsw >>> 16);
lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
lsw = lsw =
(a.highOrder & 0xffff) + (a.highOrder & 0xffff) +
(b.highOrder & 0xffff) + (b.highOrder & 0xffff) +
(c.highOrder & 0xffff) + (c.highOrder & 0xffff) +
(d.highOrder & 0xffff) + (d.highOrder & 0xffff) +
(msw >>> 16); (msw >>> 16);
msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); msw =
highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); (a.highOrder >>> 16) +
(b.highOrder >>> 16) +
(c.highOrder >>> 16) +
(d.highOrder >>> 16) +
(lsw >>> 16);
highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
return new int64(highOrder, lowOrder); return new int64(highOrder, lowOrder);
} }
function safe_add_5(a, b, c, d, e) { function safe_add_5(a, b, c, d, e) {
let lsw, msw, lowOrder, highOrder; let lsw, msw, lowOrder, highOrder;
lsw = lsw =
(a.lowOrder & 0xffff) + (a.lowOrder & 0xffff) +
(b.lowOrder & 0xffff) + (b.lowOrder & 0xffff) +
(c.lowOrder & 0xffff) + (c.lowOrder & 0xffff) +
(d.lowOrder & 0xffff) + (d.lowOrder & 0xffff) +
(e.lowOrder & 0xffff); (e.lowOrder & 0xffff);
msw = msw =
(a.lowOrder >>> 16) + (a.lowOrder >>> 16) +
(b.lowOrder >>> 16) + (b.lowOrder >>> 16) +
(c.lowOrder >>> 16) + (c.lowOrder >>> 16) +
(d.lowOrder >>> 16) + (d.lowOrder >>> 16) +
(e.lowOrder >>> 16) + (e.lowOrder >>> 16) +
(lsw >>> 16); (lsw >>> 16);
lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); lowOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
lsw = lsw =
(a.highOrder & 0xffff) + (a.highOrder & 0xffff) +
(b.highOrder & 0xffff) + (b.highOrder & 0xffff) +
(c.highOrder & 0xffff) + (c.highOrder & 0xffff) +
(d.highOrder & 0xffff) + (d.highOrder & 0xffff) +
(e.highOrder & 0xffff) + (e.highOrder & 0xffff) +
(msw >>> 16); (msw >>> 16);
msw = msw =
(a.highOrder >>> 16) + (a.highOrder >>> 16) +
(b.highOrder >>> 16) + (b.highOrder >>> 16) +
(c.highOrder >>> 16) + (c.highOrder >>> 16) +
(d.highOrder >>> 16) + (d.highOrder >>> 16) +
(e.highOrder >>> 16) + (e.highOrder >>> 16) +
(lsw >>> 16); (lsw >>> 16);
highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff); highOrder = ((msw & 0xffff) << 16) | (lsw & 0xffff);
return new int64(highOrder, lowOrder); return new int64(highOrder, lowOrder);
} }
function maj(x, y, z) { function maj(x, y, z) {
return new int64( return new int64(
(x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder), (x.highOrder & y.highOrder) ^
(x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder) (x.highOrder & z.highOrder) ^
); (y.highOrder & z.highOrder),
} (x.lowOrder & y.lowOrder) ^
(x.lowOrder & z.lowOrder) ^
(y.lowOrder & z.lowOrder)
);
}
function ch(x, y, z) { function ch(x, y, z) {
return new int64( return new int64(
(x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
(x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
); );
} }
function rotr(x, n) { function rotr(x, n) {
if (n <= 32) { if (n <= 32) {
return new int64( return new int64(
(x.highOrder >>> n) | (x.lowOrder << (32 - n)), (x.highOrder >>> n) | (x.lowOrder << (32 - n)),
(x.lowOrder >>> n) | (x.highOrder << (32 - n)) (x.lowOrder >>> n) | (x.highOrder << (32 - n))
); );
} else { } else {
return new int64( return new int64(
(x.lowOrder >>> n) | (x.highOrder << (32 - n)), (x.lowOrder >>> n) | (x.highOrder << (32 - n)),
(x.highOrder >>> n) | (x.lowOrder << (32 - n)) (x.highOrder >>> n) | (x.lowOrder << (32 - n))
); );
} }
} }
function sigma0(x) { function sigma0(x) {
let rotr28 = rotr(x, 28); let rotr28 = rotr(x, 28);
let rotr34 = rotr(x, 34); let rotr34 = rotr(x, 34);
let rotr39 = rotr(x, 39); let rotr39 = rotr(x, 39);
return new int64( return new int64(
rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
); );
} }
function sigma1(x) { function sigma1(x) {
let rotr14 = rotr(x, 14); let rotr14 = rotr(x, 14);
let rotr18 = rotr(x, 18); let rotr18 = rotr(x, 18);
let rotr41 = rotr(x, 41); let rotr41 = rotr(x, 41);
return new int64( return new int64(
rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
); );
} }
function gamma0(x) { function gamma0(x) {
let rotr1 = rotr(x, 1), let rotr1 = rotr(x, 1),
rotr8 = rotr(x, 8), rotr8 = rotr(x, 8),
shr7 = shr(x, 7); shr7 = shr(x, 7);
return new int64( return new int64(
rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
); );
} }
function gamma1(x) { function gamma1(x) {
let rotr19 = rotr(x, 19); let rotr19 = rotr(x, 19);
let rotr61 = rotr(x, 61); let rotr61 = rotr(x, 61);
let shr6 = shr(x, 6); let shr6 = shr(x, 6);
return new int64( return new int64(
rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
); );
} }
function shr(x, n) { function shr(x, n) {
if (n <= 32) { if (n <= 32) {
return new int64(x.highOrder >>> n, (x.lowOrder >>> n) | (x.highOrder << (32 - n))); return new int64(
} else { x.highOrder >>> n,
return new int64(0, x.highOrder << (32 - n)); (x.lowOrder >>> n) | (x.highOrder << (32 - n))
} );
} } else {
return new int64(0, x.highOrder << (32 - n));
}
}
str = utf8_encode(str); str = utf8_encode(str);
let strlen = str.length * charsize; let strlen = str.length * charsize;
str = str2binb(str); str = str2binb(str);
str[strlen >> 5] |= 0x80 << (24 - (strlen % 32)); str[strlen >> 5] |= 0x80 << (24 - (strlen % 32));
str[(((strlen + 128) >> 10) << 5) + 31] = strlen; str[(((strlen + 128) >> 10) << 5) + 31] = strlen;
for (let i = 0; i < str.length; i += 32) { for (let i = 0; i < str.length; i += 32) {
a = H[0]; a = H[0];
b = H[1]; b = H[1];
c = H[2]; c = H[2];
d = H[3]; d = H[3];
e = H[4]; e = H[4];
f = H[5]; f = H[5];
g = H[6]; g = H[6];
h = H[7]; h = H[7];
for (let j = 0; j < 80; j++) { for (let j = 0; j < 80; j++) {
if (j < 16) { if (j < 16) {
W[j] = new int64(str[j * 2 + i], str[j * 2 + i + 1]); W[j] = new int64(str[j * 2 + i], str[j * 2 + i + 1]);
} else { } else {
W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]); W[j] = safe_add_4(
} gamma1(W[j - 2]),
W[j - 7],
gamma0(W[j - 15]),
W[j - 16]
);
}
T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]); T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
T2 = safe_add_2(sigma0(a), maj(a, b, c)); T2 = safe_add_2(sigma0(a), maj(a, b, c));
h = g; h = g;
g = f; g = f;
f = e; f = e;
e = safe_add_2(d, T1); e = safe_add_2(d, T1);
d = c; d = c;
c = b; c = b;
b = a; b = a;
a = safe_add_2(T1, T2); a = safe_add_2(T1, T2);
} }
H[0] = safe_add_2(a, H[0]); H[0] = safe_add_2(a, H[0]);
H[1] = safe_add_2(b, H[1]); H[1] = safe_add_2(b, H[1]);
H[2] = safe_add_2(c, H[2]); H[2] = safe_add_2(c, H[2]);
H[3] = safe_add_2(d, H[3]); H[3] = safe_add_2(d, H[3]);
H[4] = safe_add_2(e, H[4]); H[4] = safe_add_2(e, H[4]);
H[5] = safe_add_2(f, H[5]); H[5] = safe_add_2(f, H[5]);
H[6] = safe_add_2(g, H[6]); H[6] = safe_add_2(g, H[6]);
H[7] = safe_add_2(h, H[7]); H[7] = safe_add_2(h, H[7]);
} }
const binarray: any = []; const binarray: any = [];
for (let i = 0; i < H.length; i++) { for (let i = 0; i < H.length; i++) {
binarray.push(H[i].highOrder); binarray.push(H[i].highOrder);
binarray.push(H[i].lowOrder); binarray.push(H[i].lowOrder);
} }
return binb2hex(binarray); return binb2hex(binarray);
} }

View File

@ -1,98 +1,97 @@
export function isNumeric(val: string): boolean { export function isNumeric(val: string): boolean {
return !isNaN(Number(val)); return !isNaN(Number(val));
} }
export function isNumber(data: any): data is number { export function isNumber(data: any): data is number {
return typeof data === 'number'; return typeof data === 'number';
} }
export function isNumberFinite(data: any): data is number { export function isNumberFinite(data: any): data is number {
return isNumber(data) && isFinite(data); return isNumber(data) && isFinite(data);
} }
export function isBoolean(data: any): data is boolean { export function isBoolean(data: any): data is boolean {
return typeof data === 'boolean'; return typeof data === 'boolean';
} }
export function isString(data: any): data is string { export function isString(data: any): data is string {
return typeof data === 'string'; return typeof data === 'string';
} }
export function isArray(data: any): data is any[] { export function isArray(data: any): data is any[] {
return Array.isArray(data); return Array.isArray(data);
} }
export function isNull(data: any): data is null { export function isNull(data: any): data is null {
return data === null; return data === null;
} }
export function isUndefined(data: any): data is undefined { export function isUndefined(data: any): data is undefined {
return data === undefined; return data === undefined;
} }
export function isNullOrUndefined(data: any): data is undefined | null { export function isNullOrUndefined(data: any): data is undefined | null {
return data === undefined || data === null; return data === undefined || data === null;
} }
export function isStringNullOrUndefined(data: any): data is undefined | null { export function isStringNullOrUndefined(data: any): data is undefined | null {
return data === 'undefined' || data === 'null' || data === ''; return data === 'undefined' || data === 'null' || data === '';
} }
export function isObject(data: any): data is any { export function isObject(data: any): data is any {
return !isNullOrUndefined(data) && typeof data === 'object' && !isArray(data); return !isNullOrUndefined(data) && typeof data === 'object' && !isArray(data);
} }
export function isInArray(data: any, listElement: any[]): boolean { export function isInArray(data: any, listElement: any[]): boolean {
for (let iii = 0; iii < listElement.length; iii++) { for (let iii = 0; iii < listElement.length; iii++) {
if (listElement[iii] === data) { if (listElement[iii] === data) {
return true; return true;
} }
} }
return false; return false;
} }
export function isArrayOf<TYPE>( export function isArrayOf<TYPE>(
data: any, data: any,
typeChecker: (subData: any) => subData is TYPE, typeChecker: (subData: any) => subData is TYPE,
length?: number length?: number
): data is TYPE[] { ): data is TYPE[] {
if (!isArray(data)) { if (!isArray(data)) {
return false; return false;
} }
if (!data.every(typeChecker)) { if (!data.every(typeChecker)) {
return false; return false;
} }
if (!isUndefined(length) && data.length != length) { if (!isUndefined(length) && data.length != length) {
return false; return false;
} }
return true; return true;
} }
export function isArrayOfs<TYPE, TYPE2, TYPE3>( export function isArrayOfs<TYPE, TYPE2, TYPE3>(
data: any, data: any,
typeChecker: (subData: any) => subData is TYPE, typeChecker: (subData: any) => subData is TYPE,
typeChecker2?: (subData: any) => subData is TYPE2, typeChecker2?: (subData: any) => subData is TYPE2,
typeChecker3?: (subData: any) => subData is TYPE3 typeChecker3?: (subData: any) => subData is TYPE3
): data is (TYPE | TYPE2 | TYPE3)[] { ): data is (TYPE | TYPE2 | TYPE3)[] {
if (!isArray(data)) { if (!isArray(data)) {
return false; return false;
} }
for (let iii = 0; iii < data.length; iii++) { for (let iii = 0; iii < data.length; iii++) {
let elem = data[iii]; let elem = data[iii];
if (typeChecker(elem)) { if (typeChecker(elem)) {
continue; continue;
} }
if (typeChecker2 != undefined && typeChecker2(elem)) { if (typeChecker2 != undefined && typeChecker2(elem)) {
continue; continue;
} }
if (typeChecker3 != undefined && typeChecker3(elem)) { if (typeChecker3 != undefined && typeChecker3(elem)) {
continue; continue;
} }
return false; return false;
} }
return true; return true;
} }
export function isOptionalOf<TYPE>( export function isOptionalOf<TYPE>(
data: any, data: any,
typeChecker: (subData: any) => subData is TYPE typeChecker: (subData: any) => subData is TYPE
): data is TYPE | undefined { ): data is TYPE | undefined {
return isUndefined(data) || typeChecker(data); return isUndefined(data) || typeChecker(data);
} }
export function isOptionalArrayOf<TYPE>( export function isOptionalArrayOf<TYPE>(
data: any, data: any,
typeChecker: (subData: any) => subData is TYPE typeChecker: (subData: any) => subData is TYPE
): data is TYPE[] | undefined { ): data is TYPE[] | undefined {
return isUndefined(data) || isArrayOf(data, typeChecker); return isUndefined(data) || isArrayOf(data, typeChecker);
} }