Compare commits
2 Commits
12223347d3
...
83bfeda4ca
Author | SHA1 | Date | |
---|---|---|---|
83bfeda4ca | |||
c489fabb77 |
@ -29,7 +29,14 @@
|
|||||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@chakra-ui/cli": "3.3.1",
|
||||||
|
"@chakra-ui/react": "3.3.1",
|
||||||
|
"@emotion/react": "11.14.0",
|
||||||
|
"allotment": "1.20.2",
|
||||||
|
"css-mediaquery": "0.1.2",
|
||||||
|
"dayjs": "1.11.13",
|
||||||
"history": "5.3.0",
|
"history": "5.3.0",
|
||||||
|
"next-themes": "^0.4.4",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-error-boundary": "5.0.0",
|
"react-error-boundary": "5.0.0",
|
||||||
@ -41,6 +48,7 @@
|
|||||||
"zustand": "5.0.3"
|
"zustand": "5.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@chakra-ui/styled-system": "^2.12.0",
|
||||||
"@playwright/test": "1.49.1",
|
"@playwright/test": "1.49.1",
|
||||||
"@storybook/addon-actions": "8.4.7",
|
"@storybook/addon-actions": "8.4.7",
|
||||||
"@storybook/addon-essentials": "8.4.7",
|
"@storybook/addon-essentials": "8.4.7",
|
||||||
@ -72,6 +80,7 @@
|
|||||||
"lint-staged": "15.3.0",
|
"lint-staged": "15.3.0",
|
||||||
"npm-check-updates": "^17.1.13",
|
"npm-check-updates": "^17.1.13",
|
||||||
"prettier": "3.4.2",
|
"prettier": "3.4.2",
|
||||||
|
"puppeteer": "24.0.0",
|
||||||
"react-is": "19.0.0",
|
"react-is": "19.0.0",
|
||||||
"storybook": "8.4.7",
|
"storybook": "8.4.7",
|
||||||
"ts-node": "10.9.2",
|
"ts-node": "10.9.2",
|
||||||
|
2068
front/pnpm-lock.yaml
generated
2068
front/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,109 +1,128 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ChakraProvider,
|
||||||
|
DialogBody,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogRoot,
|
||||||
|
DialogTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectRoot,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValueText,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { environment } from '@/environment';
|
||||||
import { App as SpaApp } from '@/scene/App';
|
import { App as SpaApp } from '@/scene/App';
|
||||||
import { FullPage } from './ui';
|
import { USERS, USERS_COLLECTION } from '@/service/session';
|
||||||
|
import { hashLocalData } from '@/utils/sso';
|
||||||
|
import { Toaster } from './components/ui/toaster';
|
||||||
|
import { systemTheme } from './theme/theme';
|
||||||
|
import { CloseButton } from './components/ui/close-button';
|
||||||
|
|
||||||
// const AppEnvHint = () => {
|
const AppEnvHint = () => {
|
||||||
// const dialog = useDisclosure();
|
const dialog = useDisclosure();
|
||||||
// const [selectUserTest, setSelectUserTest] = useState<string>('NO_USER');
|
const [selectUserTest, setSelectUserTest] = useState<string>('NO_USER');
|
||||||
// //const setUser = useRightsStore((store) => store.setUser);
|
//const setUser = useRightsStore((store) => store.setUser);
|
||||||
// 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;
|
||||||
// }
|
}
|
||||||
// const handleChange = (selectedOption) => {
|
const handleChange = (selectedOption) => {
|
||||||
// console.log(`SELECT: [${selectedOption.target.value}]`);
|
console.log(`SELECT: [${selectedOption.target.value}]`);
|
||||||
// setSelectUserTest(selectedOption.target.value);
|
setSelectUserTest(selectedOption.target.value);
|
||||||
// };
|
};
|
||||||
// const onClose = () => {
|
const onClose = () => {
|
||||||
// dialog.onClose();
|
dialog.onClose();
|
||||||
// if (selectUserTest == 'NO_USER') {
|
if (selectUserTest == 'NO_USER') {
|
||||||
// window.location.href = `/${environment.applName}/sso/${hashLocalData()}/false/__LOGOUT__`;
|
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/false/__LOGOUT__`;
|
||||||
// } else {
|
} else {
|
||||||
// window.location.href = `/${environment.applName}/sso/${hashLocalData()}/true/${USERS[selectUserTest]}`;
|
window.location.href = `/${environment.applName}/sso/${hashLocalData()}/true/${USERS[selectUserTest]}`;
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
|
|
||||||
// return (
|
return (
|
||||||
// <>
|
<>
|
||||||
// <div style={{
|
<Box
|
||||||
// zIndex: "100000",
|
as="button"
|
||||||
// position: "fixed",
|
zIndex="100000"
|
||||||
// top: "0",
|
position="fixed"
|
||||||
// height: "2px",
|
top="0"
|
||||||
// background: "warning.400",
|
insetStart="0"
|
||||||
// cursor: "pointer"
|
insetEnd="0"
|
||||||
// }}
|
h="2px"
|
||||||
// data-test-id="devtools"
|
bg="warning.400"
|
||||||
// onClick={dialog.onOpen}
|
cursor="pointer"
|
||||||
// >
|
data-test-id="devtools"
|
||||||
// <div style={{
|
onClick={dialog.onOpen}
|
||||||
// position: "fixed",
|
>
|
||||||
// top: "0",
|
<Text
|
||||||
// background: "warning.400",
|
position="fixed"
|
||||||
// color: "warning.900",
|
top="0"
|
||||||
// fontSize: "0.6rem",
|
insetStart="4"
|
||||||
// fontWeight: "bold",
|
bg="warning.400"
|
||||||
// paddingLeft: "10px",
|
color="warning.900"
|
||||||
// paddingRight: "10px",
|
fontSize="0.6rem"
|
||||||
// marginLeft: "25%",
|
fontWeight="bold"
|
||||||
// borderRadius: "10px 0 10px 0",
|
px="10px"
|
||||||
// textTransform: "uppercase",
|
marginLeft="25%"
|
||||||
// }}
|
borderBottomStartRadius="sm"
|
||||||
// >
|
borderBottomEndRadius="sm"
|
||||||
// {envName.join(' : ')}
|
textTransform="uppercase"
|
||||||
// </div>
|
>
|
||||||
// </div>
|
{envName.join(' : ')}
|
||||||
// <Dialog.Root open={dialog.open} onOpenChange={dialog.onClose}>
|
</Text>
|
||||||
// <Dialog.Trigger asChild>
|
</Box >
|
||||||
// <Button>
|
<DialogRoot open={dialog.open} onOpenChange={dialog.onClose}>
|
||||||
// {dialog.open ? "Close" : "Open"} Dialog
|
<DialogContent>
|
||||||
// </Button>
|
<DialogHeader>Outils développeurs</DialogHeader>
|
||||||
// </Dialog.Trigger>
|
<DialogBody>
|
||||||
// <Portal>
|
<Stack>
|
||||||
// <Dialog.Backdrop />
|
<Text>User</Text>
|
||||||
// <Dialog.Positioner>
|
<SelectRoot onChange={handleChange} collection={USERS_COLLECTION}>
|
||||||
// <Dialog.Content>
|
<SelectTrigger>
|
||||||
// <Dialog.Title>Outils développeurs</Dialog.Title>
|
<SelectValueText placeholder="Select test user" />
|
||||||
// <Dialog.Description>
|
</SelectTrigger>
|
||||||
// <HStack>
|
<SelectContent>
|
||||||
// <Text>User</Text>
|
{USERS_COLLECTION.items.map((value) => (
|
||||||
// <Select.Root onChange={handleChange} collection={USERS_COLLECTION}>
|
<SelectItem item={value} key={value.value}>
|
||||||
// <Select.Trigger>
|
{value.label}
|
||||||
// <SelectValueText placeholder="Select test user" />
|
</SelectItem>
|
||||||
// </Select.Trigger>
|
))}
|
||||||
// <Select.Content>
|
</SelectContent>
|
||||||
// {USERS_COLLECTION.items.map((value) => (
|
</SelectRoot>
|
||||||
// <Select.Item item={value} key={value.value}>
|
</Stack>
|
||||||
// {value.label}
|
</DialogBody>
|
||||||
// </Select.Item>
|
<DialogFooter>
|
||||||
// ))}
|
<Button onClick={onClose}>Close</Button>
|
||||||
// </Select.Content>
|
</DialogFooter>
|
||||||
// </Select.Root>
|
</DialogContent>
|
||||||
// </HStack>
|
</DialogRoot>
|
||||||
// </Dialog.Description>
|
</>
|
||||||
// <Dialog.CloseTrigger>
|
);
|
||||||
// <Button onClick={onClose}>Apply</Button>
|
};
|
||||||
// </Dialog.CloseTrigger>
|
|
||||||
// </Dialog.Content>
|
|
||||||
// </Dialog.Positioner>
|
|
||||||
// </Portal>
|
|
||||||
// </Dialog.Root>
|
|
||||||
// </>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FullPage data-test-id="Full-root-page">
|
<ChakraProvider value={systemTheme}>
|
||||||
{/* <AppEnvHint /> */}
|
<AppEnvHint />
|
||||||
<SpaApp data-test-id="app" />
|
<SpaApp />
|
||||||
{/* <Toaster /> */}
|
<Toaster />
|
||||||
</FullPage>
|
</ChakraProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
import { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
IconButton,
|
||||||
|
SliderTrack,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
MdFastForward,
|
MdFastForward,
|
||||||
MdFastRewind,
|
MdFastRewind,
|
||||||
@ -22,10 +28,10 @@ import { useSpecificArtists } from '@/service/Artist';
|
|||||||
import { useSpecificGender } from '@/service/Gender';
|
import { useSpecificGender } from '@/service/Gender';
|
||||||
import { useSpecificTrack } from '@/service/Track';
|
import { useSpecificTrack } from '@/service/Track';
|
||||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { Flex, Text } from '@/ui';
|
import { Slider } from './ui/slider';
|
||||||
|
|
||||||
export enum PlayMode {
|
export enum PlayMode {
|
||||||
PLAY_ONE,
|
PLAY_ONE,
|
||||||
@ -35,10 +41,10 @@ export enum PlayMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const playModeIcon = {
|
const playModeIcon = {
|
||||||
[PlayMode.PLAY_ONE]: <MdLooksOne size="30px" />,
|
[PlayMode.PLAY_ONE]: <MdLooksOne style={{ width: "100%", height: "100%" }} />,
|
||||||
[PlayMode.PLAY_ALL]: <MdTrendingFlat size="30px" />,
|
[PlayMode.PLAY_ALL]: <MdTrendingFlat style={{ width: "100%", height: "100%" }} />,
|
||||||
[PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne size="30px" />,
|
[PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne style={{ width: "100%", height: "100%" }} />,
|
||||||
[PlayMode.PLAY_ALL_LOOP]: <MdRepeat size="30px" />,
|
[PlayMode.PLAY_ALL_LOOP]: <MdRepeat style={{ width: "100%", height: "100%" }} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AudioPlayerProps = {};
|
export type AudioPlayerProps = {};
|
||||||
@ -77,12 +83,11 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
|||||||
: ''
|
: ''
|
||||||
);
|
);
|
||||||
}, [dataTrack, setMediaSource]);
|
}, [dataTrack, setMediaSource]);
|
||||||
const backColor = useColorThemeValue('back.100', 'back.800');
|
const backColor = useColorModeValue('back.100', 'back.800');
|
||||||
const configButton = {
|
const configButton = {
|
||||||
borderRadius: 'full',
|
borderRadius: 'full',
|
||||||
backgroundColor: '#00000000',
|
backgroundColor: 'transparent',
|
||||||
_hover: {
|
_hover: {
|
||||||
boxShadow: 'outline-over',
|
|
||||||
bgColor: 'brand.500',
|
bgColor: 'brand.500',
|
||||||
},
|
},
|
||||||
width: "50px",
|
width: "50px",
|
||||||
@ -200,133 +205,141 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
|||||||
const onChangeStateToPause = () => {
|
const onChangeStateToPause = () => {
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
};
|
};
|
||||||
|
const marks = () => {
|
||||||
|
const minutes = Math.floor(duration / 60);
|
||||||
|
const result: number[] = [];
|
||||||
|
for (let i = 1; i <= minutes; i++) {
|
||||||
|
result.push(60 * i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isNullOrUndefined(trackOffset) && (
|
{!isNullOrUndefined(trackOffset) && (
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
position="absolute"
|
||||||
position: "absolute",
|
height="150px"
|
||||||
height: "150px",
|
minHeight="150px"
|
||||||
minHeight: "150px",
|
paddingY="5px"
|
||||||
padding: "5px 10px 5px 10px",
|
paddingX="10px"
|
||||||
margin: "0 15px 0 15px",
|
marginX="15px"
|
||||||
bottom: 0,
|
bottom={0}
|
||||||
left: 0,
|
left={0}
|
||||||
right: 0,
|
right={0}
|
||||||
zIndex: 1000,
|
zIndex={1000}
|
||||||
borderWidth: "1px",
|
borderWidth="1px"
|
||||||
borderColor: "brand.900",
|
borderColor="brand.900"
|
||||||
backgroundColor: backColor,
|
bgColor={backColor}
|
||||||
borderRadius: "10px 10px 0 0",
|
borderTopRadius="10px"
|
||||||
}}
|
|
||||||
direction="column"
|
direction="column"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
|
alignContent="left"
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
style={{
|
userSelect="none"
|
||||||
alignContent: "left",
|
marginRight="auto"
|
||||||
userSelect: "none",
|
overflow="hidden"
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
// noOfLines={1}
|
// noOfLines={1}
|
||||||
>
|
>
|
||||||
{dataTrack?.name ?? '???'}
|
{dataTrack?.name ?? '???'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
|
alignContent="left"
|
||||||
fontSize="16px"
|
fontSize="16px"
|
||||||
style={{
|
userSelect="none"
|
||||||
alignContent: "left",
|
marginRight="auto"
|
||||||
userSelect: "none",
|
overflow="hidden"
|
||||||
marginRight: "auto",
|
// noOfLines={1}
|
||||||
overflow: "hidden",
|
|
||||||
//noOfLines:1
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{dataArtists.map((data) => data.name).join(', ')} /{' '}
|
{dataArtists.map((data) => data.name).join(', ')} /{' '}
|
||||||
{dataAlbum && dataAlbum?.name}
|
{dataAlbum && dataAlbum?.name}
|
||||||
{dataGender && ` / ${dataGender.name}`}
|
{dataGender && ` / ${dataGender.name}`}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex style={{ width: "full", padding: "0 15px 0 15px" }}>
|
<Box width="full" paddingX="15px">
|
||||||
<>TODO ... </>
|
<Slider
|
||||||
{/* <Slider.Root
|
|
||||||
defaultValue={[0]}
|
defaultValue={[0]}
|
||||||
value={[timeProgress]}
|
value={[timeProgress]}
|
||||||
min={0}
|
min={0}
|
||||||
max={duration}
|
max={duration}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
onChange={onSeek}
|
onValueChange={(e) => onSeek(e.value)}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
// focusThumbOnChange={false}
|
colorPalette="brand"
|
||||||
|
marks={marks()}
|
||||||
|
//focusCapture={false}
|
||||||
>
|
>
|
||||||
<SliderTrack bg="gray.200" height="10px" borderRadius="full">
|
<SliderTrack bg="brand.200" height="10px" borderRadius="full">
|
||||||
{/ * <SliderFilledTrack bg="brand.600" /> * /}
|
|
||||||
</SliderTrack>
|
</SliderTrack>
|
||||||
</Slider.Root> */}
|
</Slider>
|
||||||
</Flex>
|
</Box>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Text
|
<Text
|
||||||
|
alignContent="left"
|
||||||
fontSize="16px"
|
fontSize="16px"
|
||||||
style={{
|
userSelect="none"
|
||||||
alignContent: "left",
|
marginRight="auto"
|
||||||
userSelect: "none",
|
overflow="hidden"
|
||||||
marginRight: "auto",
|
// noOfLines={1}
|
||||||
overflow: "hidden",
|
|
||||||
// noOfLines={1},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{formatTime(timeProgress)}
|
{formatTime(timeProgress)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="16px" style={{ alignContent: "left", userSelect: "none" }}>
|
<Text alignContent="left" fontSize="16px" userSelect="none">
|
||||||
{formatTime(duration)}
|
{formatTime(duration)}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
{/* <Flex gap="5px">
|
<Flex gap="5px">
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'Play'}
|
aria-label={'Play'}
|
||||||
onClick={onPlay}
|
onClick={onPlay}
|
||||||
|
variant="ghost"
|
||||||
>
|
>
|
||||||
{isPlaying ? (
|
{isPlaying ? (
|
||||||
<MdPause size="30px" />
|
<MdPause style={{ width: "100%", height: "100%" }} />
|
||||||
) : (
|
) : (
|
||||||
<MdPlayArrow size="30px" />
|
<MdPlayArrow style={{ width: "100%", height: "100%" }} />
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'Stop'}
|
aria-label={'Stop'}
|
||||||
onClick={onStop}
|
onClick={onStop}
|
||||||
><MdStop size="30px" /></IconButton>
|
variant="ghost"
|
||||||
|
><MdStop style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'Previous track'}
|
aria-label={'Previous track'}
|
||||||
onClick={onNavigatePrevious}
|
onClick={onNavigatePrevious}
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
><MdNavigateBefore size="30px" /> </IconButton>
|
variant="ghost"
|
||||||
|
><MdNavigateBefore style={{ width: "100%", height: "100%" }} /> </IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'jump 15sec in past'}
|
aria-label={'jump 15sec in past'}
|
||||||
onClick={onFastRewind}
|
onClick={onFastRewind}
|
||||||
><MdFastRewind size="30px" /></IconButton>
|
variant="ghost"
|
||||||
|
><MdFastRewind style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'jump 15sec in future'}
|
aria-label={'jump 15sec in future'}
|
||||||
onClick={onFastForward}
|
onClick={onFastForward}
|
||||||
|
variant="ghost"
|
||||||
><MdFastForward style={{ width: "100%", height: "100%" }} /></IconButton>
|
><MdFastForward style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'Next track'}
|
aria-label={'Next track'}
|
||||||
marginRight="auto"
|
marginRight="auto"
|
||||||
onClick={onNavigateNext}
|
onClick={onNavigateNext}
|
||||||
|
variant="ghost"
|
||||||
><MdNavigateNext style={{ width: "100%", height: "100%" }} /></IconButton>
|
><MdNavigateNext style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
{...configButton}
|
{...configButton}
|
||||||
aria-label={'continue to the end'}
|
aria-label={'continue to the end'}
|
||||||
onClick={onTypePlay}
|
onClick={onTypePlay}
|
||||||
|
variant="ghost"
|
||||||
>{playModeIcon[playingMode]}</IconButton>
|
>{playModeIcon[playingMode]}</IconButton>
|
||||||
</Flex> */}
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { CSSProperties, ReactElement, useEffect, useState } from 'react';
|
import { ReactElement, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { Box, BoxProps, Flex, FlexProps } from '@chakra-ui/react';
|
||||||
|
import { Image } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { ObjectId } from '@/back-api';
|
import { ObjectId } from '@/back-api';
|
||||||
import { DivProps, Flex } from '@/ui';
|
|
||||||
|
|
||||||
export type CoversProps = Omit<DivProps, "iconEmpty"> & {
|
export type CoversProps = Omit<BoxProps, "iconEmpty"> & {
|
||||||
data?: ObjectId[];
|
data?: ObjectId[];
|
||||||
size?: CSSProperties["width"];
|
size?: BoxProps["width"];
|
||||||
iconEmpty?: ReactElement;
|
iconEmpty?: ReactElement;
|
||||||
slideshow?: boolean;
|
slideshow?: boolean;
|
||||||
};
|
};
|
||||||
@ -43,28 +45,26 @@ export const Covers = ({
|
|||||||
return <Icon icon={iconEmpty} sizeIcon={size} />;
|
return <Icon icon={iconEmpty} sizeIcon={size} />;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Box
|
||||||
style={{
|
width={size}
|
||||||
width: size,
|
height={size}
|
||||||
height: size,
|
minHeight={size}
|
||||||
minHeight: size,
|
minWidth={size}
|
||||||
minWidth: size,
|
borderColor="blue"
|
||||||
borderColor: "blue",
|
borderWidth="1px"
|
||||||
borderWidth: "1px",
|
margin="auto"
|
||||||
margin: "auto",
|
|
||||||
}}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
></Flex>
|
></Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 <Flex
|
||||||
position="relative"
|
position="relative"
|
||||||
// {...rest}
|
// {...rest}
|
||||||
maxWidth={size}
|
maxWidth={size}
|
||||||
@ -95,5 +95,5 @@ export const Covers = ({
|
|||||||
opacity={topOpacity}
|
opacity={topOpacity}
|
||||||
zIndex={2}
|
zIndex={2}
|
||||||
/>
|
/>
|
||||||
</Flex>*/
|
</Flex>
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { Flex } from "@/ui";
|
import { Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
export const EmptyEnd = () => {
|
export const EmptyEnd = () => {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Box
|
||||||
style={{
|
width="full"
|
||||||
width: "full",
|
height="25%"
|
||||||
height: "25%",
|
minHeight="250px"
|
||||||
minHeight: "250px",
|
|
||||||
}}
|
|
||||||
// borderWidth="1px"
|
// borderWidth="1px"
|
||||||
// borderColor="red"
|
// borderColor="red"
|
||||||
></Flex>
|
></Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import {
|
||||||
import { Flex, FlexProps } from '@/ui';
|
Box,
|
||||||
|
Flex,
|
||||||
|
FlexProps,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { forwardRef, ReactNode } from 'react';
|
import { forwardRef, ReactNode } from 'react';
|
||||||
|
|
||||||
export type IconProps = FlexProps & {
|
export type IconProps = FlexProps & {
|
||||||
@ -9,31 +12,27 @@ export type IconProps = FlexProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Icon = forwardRef<HTMLDivElement, IconProps>(
|
export const Icon = forwardRef<HTMLDivElement, IconProps>(
|
||||||
({ icon: IconEl, color, sizeIcon = '1em', style, ...rest }, ref) => {
|
({ icon: IconEl, color, sizeIcon = '1em', ...rest }, ref) => {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex flex="none"
|
||||||
|
minWidth={sizeIcon}
|
||||||
|
minHeight={sizeIcon}
|
||||||
|
maxWidth={sizeIcon}
|
||||||
|
maxHeight={sizeIcon}
|
||||||
align="center"
|
align="center"
|
||||||
style={{
|
padding="1px"
|
||||||
flex: "none",
|
ref={ref}
|
||||||
minWidth: sizeIcon,
|
|
||||||
minHeight: sizeIcon,
|
|
||||||
maxWidth: sizeIcon,
|
|
||||||
maxHeight: sizeIcon,
|
|
||||||
padding: "1px",
|
|
||||||
...style
|
|
||||||
}}
|
|
||||||
{...rest}>
|
{...rest}>
|
||||||
<Flex
|
<Box
|
||||||
style={{
|
marginX="auto"
|
||||||
margin: "0 auto 0 auto",
|
width="100%"
|
||||||
width: "100%",
|
minWidth="100%"
|
||||||
minWidth: "100%",
|
height="100%"
|
||||||
height: "100%",
|
color={color}
|
||||||
color: color,
|
asChild
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{IconEl}
|
{IconEl}
|
||||||
</Flex>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { ReactNode, useEffect } from 'react';
|
import React, { ReactNode, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Flex, Image } from '@chakra-ui/react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import background from '@/assets/images/ikon.svg';
|
import background from '@/assets/images/ikon.svg';
|
||||||
import { TOP_BAR_HEIGHT } from '@/components/TopBar/TopBar';
|
import { TOP_BAR_HEIGHT } from '@/components/TopBar/TopBar';
|
||||||
import { Flex } from '@/ui';
|
|
||||||
|
|
||||||
export type LayoutProps = React.PropsWithChildren<unknown> & {
|
export type LayoutProps = React.PropsWithChildren<unknown> & {
|
||||||
topBar?: ReactNode;
|
topBar?: ReactNode;
|
||||||
@ -20,33 +20,30 @@ export const PageLayout = ({ children }: LayoutProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
minH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||||
position: "absolute",
|
maxH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||||
minHeight: `calc(100vh - ${TOP_BAR_HEIGHT})`,
|
position="absolute"
|
||||||
maxHeight: `calc(100vh - ${TOP_BAR_HEIGHT})`,
|
top={TOP_BAR_HEIGHT}
|
||||||
top: TOP_BAR_HEIGHT,
|
bottom={0}
|
||||||
bottom: 0,
|
left={0}
|
||||||
left: 0,
|
right={0}
|
||||||
right: 0,
|
minWidth="300px"
|
||||||
minWidth: "300px",
|
zIndex={-1}
|
||||||
zIndex: -1,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* <Image src={background} boxSize="90%" margin="auto" opacity="30%" /> */}
|
<Image src={background} boxSize="90%" margin="auto" opacity="30%" />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
style={{
|
overflowX="auto"
|
||||||
overflow: "auto",
|
overflowY="auto"
|
||||||
minHeight: `calc(100vh - ${TOP_BAR_HEIGHT})`,
|
minH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||||
maxHeight: `calc(100vh - ${TOP_BAR_HEIGHT})`,
|
maxH={`calc(100vh - ${TOP_BAR_HEIGHT})`}
|
||||||
position: "absolute",
|
position="absolute"
|
||||||
top: TOP_BAR_HEIGHT,
|
top={TOP_BAR_HEIGHT}
|
||||||
bottom: 0,
|
bottom={0}
|
||||||
left: 0,
|
left={0}
|
||||||
right: 0,
|
right={0}
|
||||||
minWidth: "300px",
|
minWidth="300px"
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
import { CSSProperties, useEffect } from 'react';
|
import React, { ReactNode, useEffect } from 'react';
|
||||||
|
|
||||||
|
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 { colors } from '@/theme/colors';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Flex, FlexProps } from '@/ui';
|
|
||||||
|
|
||||||
export type LayoutProps = FlexProps & {
|
export type LayoutProps = FlexProps & {
|
||||||
width?: CSSProperties['width'];
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PageLayoutInfoCenter = ({
|
export const PageLayoutInfoCenter = ({
|
||||||
children,
|
children,
|
||||||
width = "75%",
|
width = '25%',
|
||||||
style,
|
|
||||||
...rest
|
...rest
|
||||||
}: LayoutProps) => {
|
}: LayoutProps) => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@ -27,17 +26,14 @@ export const PageLayoutInfoCenter = ({
|
|||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
style={{
|
margin="auto"
|
||||||
margin: "auto",
|
minWidth={width}
|
||||||
width,
|
border="back.900"
|
||||||
border: "back.900",
|
borderWidth="1px"
|
||||||
borderWidth: "1px",
|
borderRadius="8px"
|
||||||
borderRadius: "8px",
|
padding="10px"
|
||||||
padding: "10px",
|
boxShadow={'0px 0px 16px ' + colors.back[900]}
|
||||||
boxShadow: '0px 0px 16px ' + colors.back[900],
|
backgroundColor={useColorModeValue('#FFFFFF', '#000000')}
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF', '#000000'),
|
|
||||||
...style
|
|
||||||
}}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { MdSearch } from 'react-icons/md';
|
import { MdSearch } from 'react-icons/md';
|
||||||
|
|
||||||
export type SearchInputProps = {
|
export type SearchInputProps = {
|
||||||
@ -38,15 +42,15 @@ export const SearchInput = ({
|
|||||||
onSubmitValue(inputData);
|
onSubmitValue(inputData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (<></>
|
return (
|
||||||
//<Group maxWidth="200px" marginLeft="auto" {...searchInputProperty}>
|
<Group maxWidth="200px" marginLeft="auto" {...searchInputProperty}>
|
||||||
// <MdSearch color="gray.300" />
|
<MdSearch color="gray.300" />
|
||||||
// <Input
|
<Input
|
||||||
// onFocus={onFocusKeep}
|
onFocus={onFocusKeep}
|
||||||
// onBlur={() => setTimeout(() => onFocusLost(), 200)}
|
onBlur={() => setTimeout(() => onFocusLost(), 200)}
|
||||||
// onChange={onChange}
|
onChange={onChange}
|
||||||
// onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
// />
|
/>
|
||||||
//</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
HStack,
|
||||||
|
IconButton,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
Button,
|
||||||
|
ConditionalValue,
|
||||||
|
Span,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
LuAlignJustify,
|
LuAlignJustify,
|
||||||
LuArrowBigLeft,
|
LuArrowBigLeft,
|
||||||
|
LuKeySquare,
|
||||||
LuLogIn,
|
LuLogIn,
|
||||||
LuLogOut,
|
LuLogOut,
|
||||||
LuMoon,
|
LuMoon,
|
||||||
@ -15,17 +26,25 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { SessionState } from '@/service/SessionState';
|
import { SessionState } from '@/service/SessionState';
|
||||||
import { colors } from '@/theme/colors';
|
import { colors } from '@/theme/colors';
|
||||||
import { requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
import { requestOpenSite, requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||||
import { useSessionService } from '@/service/session';
|
import { useSessionService } from '@/service/session';
|
||||||
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
|
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
|
||||||
import { useColorThemeValue, useTheme } from '@/theme/ThemeContext';
|
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
import { useColorMode, useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Button, Flex, Text } from '@/ui';
|
import {
|
||||||
|
DrawerBody,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerRoot,
|
||||||
|
} from '@/components/ui/drawer';
|
||||||
|
|
||||||
export const TOP_BAR_HEIGHT = '50px';
|
export const TOP_BAR_HEIGHT = '50px';
|
||||||
|
|
||||||
export const BUTTON_TOP_BAR_PROPERTY = {
|
export const BUTTON_TOP_BAR_PROPERTY = {
|
||||||
theme: '@menu',
|
variant: "ghost" as ConditionalValue<"ghost" | "outline" | "solid" | "subtle" | "surface" | "plain" | undefined>,
|
||||||
|
//colorPalette: "brand",
|
||||||
|
fontSize: '20px',
|
||||||
|
textTransform: 'uppercase',
|
||||||
height: TOP_BAR_HEIGHT,
|
height: TOP_BAR_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,11 +54,11 @@ export type TopBarProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TopBar = ({ title, children }: TopBarProps) => {
|
export const TopBar = ({ title, children }: TopBarProps) => {
|
||||||
const { theme, toggleTheme } = useTheme();
|
const { colorMode, toggleColorMode } = useColorMode();
|
||||||
const { clearToken } = useSessionService();
|
const { clearToken } = useSessionService();
|
||||||
|
|
||||||
const { session } = useServiceContext();
|
const { session } = useServiceContext();
|
||||||
const backColor = useColorThemeValue('back.100', 'back.800');
|
const backColor = useColorModeValue('back.100', 'back.800');
|
||||||
const drawerDisclose = useDisclosure();
|
const drawerDisclose = useDisclosure();
|
||||||
const onChangeTheme = () => {
|
const onChangeTheme = () => {
|
||||||
drawerDisclose.onOpen();
|
drawerDisclose.onOpen();
|
||||||
@ -57,6 +76,9 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
|||||||
clearToken();
|
clearToken();
|
||||||
requestSignOut();
|
requestSignOut();
|
||||||
};
|
};
|
||||||
|
const onKarso = (): void => {
|
||||||
|
requestOpenSite();
|
||||||
|
};
|
||||||
const onSelectAdd = () => {
|
const onSelectAdd = () => {
|
||||||
navigate('/add');
|
navigate('/add');
|
||||||
};
|
};
|
||||||
@ -74,104 +96,106 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
height={TOP_BAR_HEIGHT}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
backgroundColor={backColor}
|
||||||
gap="2"
|
gap="2"
|
||||||
align="center"
|
px="2"
|
||||||
justify="space-between"
|
boxShadow={'0px 2px 4px ' + colors.back[900]}
|
||||||
style={{
|
zIndex={200}
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
height: TOP_BAR_HEIGHT,
|
|
||||||
backgroundColor: backColor,
|
|
||||||
padding: "0 2 0 2",
|
|
||||||
boxShadow: `0px 2px 4px ${colors.back[900]}`,
|
|
||||||
zIndex: 200,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
|
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
|
||||||
<LuAlignJustify />
|
<HStack>
|
||||||
<Text style={{ padding: "0 0 0 3px" }} fontWeight="bold">
|
<LuAlignJustify />
|
||||||
Karusic
|
<Text paddingLeft="3px" fontWeight="bold">
|
||||||
</Text>
|
Karusic
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
</Button>
|
</Button>
|
||||||
{title && (
|
{title && (
|
||||||
<Text
|
<Text
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
style={{
|
textTransform="uppercase"
|
||||||
textTransform: "uppercase",
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
userSelect="none"
|
||||||
userSelect: "none",
|
color="brand.500"
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
<Flex style={{ right: 0 }}>
|
<Flex right="0">
|
||||||
{session?.state !== SessionState.CONNECTED && (
|
{session?.state !== SessionState.CONNECTED && (
|
||||||
<>
|
<>
|
||||||
<Button {...BUTTON_TOP_BAR_PROPERTY} /*{...THEME.Button.primary}*/ onClick={onSignIn}>
|
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onSignIn}>
|
||||||
<LuLogIn />
|
<LuLogIn />
|
||||||
<Text style={{ paddingLeft: "0 0 0 3px" }} fontWeight="bold">
|
<Text paddingLeft="3px" fontWeight="bold">
|
||||||
Sign-in
|
Sign-in
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
{...BUTTON_TOP_BAR_PROPERTY}
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
onClick={onSignUp}
|
onClick={onSignUp}
|
||||||
// disabled={true}
|
disabled={true}
|
||||||
>
|
>
|
||||||
<MdMore />
|
<MdMore />
|
||||||
<Text style={{ padding: "0 0 0 3px" }} fontWeight="bold">
|
<Text paddingLeft="3px" fontWeight="bold">
|
||||||
Sign-up
|
Sign-up
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* {session?.state === SessionState.CONNECTED && (
|
{session?.state === SessionState.CONNECTED && (
|
||||||
<Menu.Root>
|
<MenuRoot>
|
||||||
<Menu.Trigger asChild>
|
<MenuTrigger asChild>
|
||||||
<IconButton
|
<IconButton
|
||||||
as={IconButton}
|
asChild
|
||||||
aria-label="Options"
|
aria-label="Options"
|
||||||
{...BUTTON_TOP_BAR_PROPERTY}
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
width={TOP_BAR_HEIGHT}
|
width={TOP_BAR_HEIGHT}
|
||||||
><MdSupervisedUserCircle /></IconButton>
|
><MdSupervisedUserCircle /></IconButton>
|
||||||
</Menu.Trigger>
|
</MenuTrigger>
|
||||||
<Menu.Content>
|
<MenuContent>
|
||||||
<Menu.Item value="user" valueText="user" _hover={{}} 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>
|
||||||
</Menu.Item>
|
</MenuItem>
|
||||||
<Menu.Item value="Settings" valueText="Settings" onClick={onSettings}><LuSettings />Settings</Menu.Item>
|
<MenuItem value="Settings" valueText="Settings" onClick={onSettings}><LuSettings />Settings</MenuItem>
|
||||||
<Menu.Item value="Help" valueText="Help" onClick={onHelp}><MdHelp /> Help</Menu.Item>
|
<MenuItem value="Help" valueText="Help" onClick={onHelp}><MdHelp /> Help</MenuItem>
|
||||||
<Menu.Item value="Sign-out" valueText="Sign-out" onClick={onSignOut}>
|
<MenuItem value="Sign-out" valueText="Sign-out" onClick={onSignOut}>
|
||||||
<LuLogOut /> Sign-out
|
<LuLogOut /> Sign-out
|
||||||
</Menu.Item>
|
</MenuItem>
|
||||||
|
<MenuItem value="karso" valueText="Karso" onClick={onKarso}>
|
||||||
|
<LuKeySquare /> Karso (SSO)
|
||||||
|
</MenuItem>
|
||||||
{colorMode === 'light' ? (
|
{colorMode === 'light' ? (
|
||||||
<Menu.Item 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
|
||||||
</Menu.Item>
|
</MenuItem>
|
||||||
) : (
|
) : (
|
||||||
<Menu.Item 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
|
||||||
</Menu.Item>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</Menu.Content>
|
</MenuContent>
|
||||||
</Menu.Root>
|
</MenuRoot>
|
||||||
)} */}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
{/* <Drawer.Root
|
<DrawerRoot
|
||||||
placement="start"
|
placement="start"
|
||||||
onOpenChange={drawerDisclose.onClose}
|
onOpenChange={drawerDisclose.onClose}
|
||||||
open={drawerDisclose.open}
|
open={drawerDisclose.open}
|
||||||
data-testid="top-bar_drawer-root"
|
data-testid="top-bar_drawer-root"
|
||||||
>
|
>
|
||||||
<Drawer.Content
|
<DrawerContent
|
||||||
data-test-id="top-bar_drawer-content">
|
data-testid="top-bar_drawer-content">
|
||||||
<Drawer.Header
|
<DrawerHeader
|
||||||
paddingY="auto"
|
paddingY="auto"
|
||||||
as="button"
|
as="button"
|
||||||
onClick={drawerDisclose.onClose}
|
onClick={drawerDisclose.onClose}
|
||||||
@ -180,52 +204,57 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
|||||||
color={useColorModeValue('brand.900', 'brand.50')}
|
color={useColorModeValue('brand.900', 'brand.50')}
|
||||||
textTransform="uppercase"
|
textTransform="uppercase"
|
||||||
>
|
>
|
||||||
<HStack height={TOP_BAR_HEIGHT}>
|
<HStack
|
||||||
|
{...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
|
||||||
<LuArrowBigLeft />
|
<LuArrowBigLeft />
|
||||||
<Text as="span" paddingLeft="3px">
|
<Span paddingLeft="3px">
|
||||||
Karusic
|
Karusic
|
||||||
</Text>
|
</Span>
|
||||||
</HStack>
|
</HStack>
|
||||||
</Drawer.Header>
|
</DrawerHeader>
|
||||||
<Drawer.Body paddingX="0px">
|
<DrawerBody paddingX="0px">
|
||||||
|
<Box marginY="3" />
|
||||||
<Button
|
<Button
|
||||||
background="#00000000"
|
background="#00000000"
|
||||||
borderRadius="0px"
|
borderRadius="0px"
|
||||||
onClick={onSelectHome}
|
onClick={onSelectHome}
|
||||||
width="full"
|
width="full"
|
||||||
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
>
|
>
|
||||||
<MdHome />
|
<MdHome style={{ width: "45px", height: "45px" }} />
|
||||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||||
Home
|
Home
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<hr />
|
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
|
||||||
<Button
|
<Button
|
||||||
background="#00000000"
|
background="#00000000"
|
||||||
borderRadius="0px"
|
borderRadius="0px"
|
||||||
onClick={onSelectOnAir}
|
onClick={onSelectOnAir}
|
||||||
width="full"
|
width="full"
|
||||||
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
>
|
>
|
||||||
<MdOutlinePlaylistPlay />
|
<MdOutlinePlaylistPlay style={{ width: "45px", height: "45px" }} />
|
||||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||||
On air
|
On air
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<hr />
|
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
|
||||||
<Button
|
<Button
|
||||||
background="#00000000"
|
background="#00000000"
|
||||||
borderRadius="0px"
|
borderRadius="0px"
|
||||||
onClick={onSelectAdd}
|
onClick={onSelectAdd}
|
||||||
width="full"
|
width="full"
|
||||||
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
>
|
>
|
||||||
<MdOutlineUploadFile />
|
<MdOutlineUploadFile style={{ width: "45px", height: "45px" }} />
|
||||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||||
Add Media
|
Add Media
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Drawer.Body>
|
</DrawerBody>
|
||||||
</Drawer.Content>
|
</DrawerContent>
|
||||||
</Drawer.Root> */}
|
</DrawerRoot>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
|
import { Flex, Span, Text } from '@chakra-ui/react';
|
||||||
import { LuDisc3 } from 'react-icons/lu';
|
import { LuDisc3 } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Album } from '@/back-api';
|
import { Album } from '@/back-api';
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
import { useCountTracksWithAlbumId } from '@/service/Track';
|
import { useCountTracksWithAlbumId } from '@/service/Track';
|
||||||
import { BASE_WRAP_ICON_SIZE } from '@/constants/genericSpacing';
|
import { BASE_WRAP_ICON_SIZE } from '@/constants/genericSpacing';
|
||||||
import { Flex, Text } from '@/ui';
|
|
||||||
import { Span } from '@/ui/Span';
|
|
||||||
|
|
||||||
export type DisplayAlbumProps = {
|
export type DisplayAlbumProps = {
|
||||||
dataAlbum?: Album;
|
dataAlbum?: Album;
|
||||||
@ -27,7 +25,7 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
|||||||
data={dataAlbum?.covers}
|
data={dataAlbum?.covers}
|
||||||
size={BASE_WRAP_ICON_SIZE}
|
size={BASE_WRAP_ICON_SIZE}
|
||||||
flex={1}
|
flex={1}
|
||||||
// TODO: iconEmpty={LuDisc3}
|
iconEmpty={<LuDisc3 />}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
@ -35,33 +33,26 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
|||||||
//maxWidth="150px"
|
//maxWidth="150px"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
style={{
|
overflowX="hidden"
|
||||||
marginRight: "auto",
|
flex={1}
|
||||||
overflow: "hidden",
|
|
||||||
flex: 1,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Span
|
<Span
|
||||||
// align="left"
|
textAlign="left"
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
userSelect="none"
|
userSelect="none"
|
||||||
style={{
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
overflow="hidden"
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
// noOfLines={[1, 2]}
|
// noOfLines={[1, 2]}
|
||||||
>
|
>
|
||||||
{dataAlbum?.name}
|
{dataAlbum?.name}
|
||||||
</Span>
|
</Span>
|
||||||
<Span
|
<Span
|
||||||
// align="left"
|
textAlign="left"
|
||||||
fontSize="15px"
|
fontSize="15px"
|
||||||
userSelect="none"
|
userSelect="none"
|
||||||
style={{
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
overflow="hidden"
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
// noOfLines={1}
|
// noOfLines={1}
|
||||||
>
|
>
|
||||||
{countTracksOfAnAlbum} track{countTracksOfAnAlbum >= 1 && 's'}
|
{countTracksOfAnAlbum} track{countTracksOfAnAlbum >= 1 && 's'}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
|
||||||
export type MenuElement = {
|
export type MenuElement = {
|
||||||
|
icon?: ReactNode;
|
||||||
name: string;
|
name: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
};
|
};
|
||||||
@ -12,28 +16,31 @@ export type ContextMenuProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ContextMenu = ({ elements }: ContextMenuProps) => {
|
export const ContextMenu = ({ elements }: ContextMenuProps) => {
|
||||||
// if (!elements) {
|
if (!elements) {
|
||||||
return <></>;
|
return <></>;
|
||||||
// }
|
}
|
||||||
// return (
|
return (
|
||||||
// <Menu.Root
|
<MenuRoot
|
||||||
// data-testid="context-menu">
|
data-testid="context-menu">
|
||||||
// <Menu.Trigger asChild
|
<MenuTrigger asChild
|
||||||
// data-testid="context-menu_trigger">
|
marginY="auto"
|
||||||
// {/* This is very stupid, we need to set as span to prevent a button in button... WTF */}
|
marginRight="4px"
|
||||||
// <Button {...THEME.Button.primary} >
|
data-testid="context-menu_trigger">
|
||||||
// <LuMenu />
|
{/* This is very stupid, we need to set as span to prevent a button in button... WTF */}
|
||||||
// </Button>
|
<Button variant="ghost" color="brand.500">
|
||||||
// </Menu.Trigger>
|
<LuMenu />
|
||||||
// <Menu.Content
|
</Button>
|
||||||
// data-testid="context-menu_content">
|
</MenuTrigger>
|
||||||
// {elements?.map((data) => (
|
<MenuContent
|
||||||
// <Menu.Item key={data.name} value={data.name} onClick={data.onClick}
|
data-testid="context-menu_content">
|
||||||
// data-testid="context-menu_item">
|
{elements?.map((data) => (
|
||||||
// {data.name}
|
<MenuItem key={data.name} value={data.name} onClick={data.onClick} height="65px" fontSize="25px"
|
||||||
// </Menu.Item>
|
data-test-id="context-menu_item" >
|
||||||
// ))}
|
{data.icon}
|
||||||
// </Menu.Content>
|
{data.name}
|
||||||
// </Menu.Root >
|
</MenuItem>
|
||||||
//);
|
))}
|
||||||
|
</MenuContent>
|
||||||
|
</MenuRoot >
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,14 @@ import {
|
|||||||
RefObject,
|
RefObject,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
|
Center,
|
||||||
|
Flex,
|
||||||
|
HStack,
|
||||||
|
Image,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
MdHighlightOff,
|
MdHighlightOff,
|
||||||
MdUploadFile,
|
MdUploadFile,
|
||||||
@ -12,7 +19,6 @@ import {
|
|||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||||
import { DataUrlAccess } from '@/utils/data-url-access';
|
import { DataUrlAccess } from '@/utils/data-url-access';
|
||||||
import { Flex, FlexProps } from '@/ui';
|
|
||||||
|
|
||||||
export type DragNdropProps = {
|
export type DragNdropProps = {
|
||||||
onFilesSelected?: (file: File[]) => void;
|
onFilesSelected?: (file: File[]) => void;
|
||||||
@ -52,42 +58,40 @@ export const DragNdrop = ({
|
|||||||
onUriSelected(listUri);
|
onUriSelected(listUri);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return <></>;
|
|
||||||
/*
|
return (
|
||||||
return (
|
<Box
|
||||||
<Box
|
width={width}
|
||||||
width={width}
|
height={height}
|
||||||
height={height}
|
border="2px"
|
||||||
border="2px"
|
borderRadius="5px"
|
||||||
borderRadius="5px"
|
borderStyle="dashed"
|
||||||
borderStyle="dashed"
|
onDrop={handleDrop}
|
||||||
onDrop={handleDrop}
|
onDragOver={(event) => event.preventDefault()}
|
||||||
onDragOver={(event) => event.preventDefault()}
|
>
|
||||||
>
|
<label htmlFor="browse">
|
||||||
<label htmlFor="browse">
|
<Box paddingY="15%" height="100%" cursor="pointer">
|
||||||
<Box paddingY="15%" height="100%" cursor="pointer">
|
<Center>
|
||||||
<Center>
|
<MdUploadFile size="50%" />
|
||||||
<MdUploadFile size="50%" />
|
</Center>
|
||||||
</Center>
|
<Center>
|
||||||
<Center>
|
<input
|
||||||
<input
|
type="file"
|
||||||
type="file"
|
hidden
|
||||||
hidden
|
id="browse"
|
||||||
id="browse"
|
onChange={handleFileChange}
|
||||||
onChange={handleFileChange}
|
//accept=".pdf,.docx,.pptx,.txt,.xlsx"
|
||||||
//accept=".pdf,.docx,.pptx,.txt,.xlsx"
|
multiple
|
||||||
multiple
|
/>
|
||||||
/>
|
Browse files
|
||||||
Browse files
|
</Center>
|
||||||
</Center>
|
</Box>
|
||||||
</Box>
|
</label>
|
||||||
</label>
|
</Box>
|
||||||
</Box>
|
);
|
||||||
);
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CenterIconProps = FlexProps & {
|
export type CenterIconProps = BoxProps & {
|
||||||
icon: any;
|
icon: any;
|
||||||
sizeIcon?: string;
|
sizeIcon?: string;
|
||||||
};
|
};
|
||||||
@ -95,22 +99,19 @@ export type CenterIconProps = FlexProps & {
|
|||||||
export const CenterIcon = ({
|
export const CenterIcon = ({
|
||||||
icon: IconEl,
|
icon: IconEl,
|
||||||
sizeIcon = '15px',
|
sizeIcon = '15px',
|
||||||
style,
|
|
||||||
...rest
|
...rest
|
||||||
}: CenterIconProps) => {
|
}: CenterIconProps) => {
|
||||||
return (
|
return (
|
||||||
<Flex style={{ position: "relative", width: sizeIcon, height: sizeIcon, flex: "none", ...style }}
|
<Box position="relative" w={sizeIcon} h={sizeIcon} flex="none" {...rest}>
|
||||||
{...rest}>
|
<Box
|
||||||
{/*<Flex
|
|
||||||
as={IconEl}
|
|
||||||
w={sizeIcon}
|
w={sizeIcon}
|
||||||
h={sizeIcon}
|
h={sizeIcon}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
top="50%"
|
top="50%"
|
||||||
left="50%"
|
left="50%"
|
||||||
transform="translate(-50%, -50%)"
|
transform="translate(-50%, -50%)"
|
||||||
/>*/}
|
>{IconEl}</Box>
|
||||||
</Flex>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -141,21 +142,20 @@ export const FormCovers = ({
|
|||||||
isModify={form.isModify[variableName]}
|
isModify={form.isModify[variableName]}
|
||||||
onRestore={() => form.restoreValue({ [variableName]: true })}
|
onRestore={() => form.restoreValue({ [variableName]: true })}
|
||||||
{...rest}
|
{...rest}
|
||||||
> <></>
|
>
|
||||||
{/* <HStack wrap="wrap" width="full">
|
<HStack wrap="wrap" width="full">
|
||||||
{urls.map((data, index) => (
|
{urls.map((data, index) => (
|
||||||
<Flex align="flex-start" key={data}>
|
<Flex align="flex-start" key={data}>
|
||||||
<Box width="125px" height="125px" position="relative">
|
<Box width="125px" height="125px" position="relative">
|
||||||
<Box width="125px" height="125px" position="absolute">
|
<Box width="125px" height="125px" position="absolute">
|
||||||
<CenterIcon
|
<CenterIcon
|
||||||
icon={MdHighlightOff}
|
|
||||||
width="125px"
|
width="125px"
|
||||||
sizeIcon="100%"
|
sizeIcon="100%"
|
||||||
zIndex="+1"
|
zIndex="+1"
|
||||||
color="#00000020"
|
color="#00000020"
|
||||||
_hover={{ color: 'red' }}
|
_hover={{ color: 'red' }}
|
||||||
onClick={() => onRemove && onRemove(index)}
|
onClick={() => onRemove && onRemove(index)}
|
||||||
/>
|
><MdHighlightOff /></CenterIcon>
|
||||||
</Box>
|
</Box>
|
||||||
<Image loading="lazy" src={data} boxSize="full" />
|
<Image loading="lazy" src={data} boxSize="full" />
|
||||||
</Box>
|
</Box>
|
||||||
@ -169,7 +169,7 @@ export const FormCovers = ({
|
|||||||
onUriSelected={onUriSelected}
|
onUriSelected={onUriSelected}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</HStack> */}
|
</HStack>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Flex, Text } from '@/ui';
|
|
||||||
import { Span } from '@/ui/Span';
|
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
import { MdErrorOutline, MdHelpOutline, MdRefresh } from 'react-icons/md';
|
import { MdErrorOutline, MdHelpOutline, MdRefresh } from 'react-icons/md';
|
||||||
|
|
||||||
export type FormGroupProps = {
|
export type FormGroupProps = {
|
||||||
@ -24,22 +23,20 @@ export const FormGroup = ({
|
|||||||
onRestore,
|
onRestore,
|
||||||
}: FormGroupProps) => (
|
}: FormGroupProps) => (
|
||||||
<Flex
|
<Flex
|
||||||
style={{
|
borderLeftWidth="3px"
|
||||||
borderLeftWidth: "3px",
|
borderLeftColor={error ? 'red' : isModify ? 'blue' : '#00000000'}
|
||||||
borderLeftColor: error ? 'red' : isModify ? 'blue' : '#00000000',
|
|
||||||
}}
|
|
||||||
paddingLeft="7px"
|
paddingLeft="7px"
|
||||||
padding="0 4px"
|
paddingY="4px"
|
||||||
direction="column"
|
direction="column"
|
||||||
>
|
>
|
||||||
<Flex direction="row" width="full" gap="52px">
|
<Flex direction="row" width="full" gap="52px">
|
||||||
{!!label && (
|
{!!label && (
|
||||||
<Text style={{ marginRight: "auto" }} fontWeight="bold">
|
<Text marginRight="auto" fontWeight="bold">
|
||||||
{label}{' '}
|
{label}{' '}
|
||||||
{isRequired && (
|
{isRequired && (
|
||||||
<Span color="red.600">
|
<Text as="span" color="red.600">
|
||||||
*
|
*
|
||||||
</Span>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
import { Input } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||||
import { Input } from '@/ui';
|
|
||||||
|
|
||||||
export type FormInputProps = {
|
export type FormInputProps = {
|
||||||
form: UseFormidableReturn;
|
form: UseFormidableReturn;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||||
|
import { NumberInputField, NumberInputProps, NumberInputRoot } from '../ui/number-input';
|
||||||
|
|
||||||
export type FormNumberProps = Pick<
|
export type FormNumberProps = Pick<
|
||||||
NumberInput.RootProps,
|
NumberInputProps,
|
||||||
'step' | 'defaultValue' | 'min' | 'max'
|
'step' | 'defaultValue' | 'min' | 'max'
|
||||||
> & {
|
> & {
|
||||||
form: UseFormidableReturn;
|
form: UseFormidableReturn;
|
||||||
@ -27,24 +27,26 @@ export const FormNumber = ({
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
...rest
|
...rest
|
||||||
}: FormNumberProps) => {
|
}: FormNumberProps) => {
|
||||||
|
const onEvent = (value) => {
|
||||||
|
form.setValues({ [variableName]: value.value });
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
isModify={form.isModify[variableName]}
|
isModify={form.isModify[variableName]}
|
||||||
onRestore={() => form.restoreValue({ [variableName]: true })}
|
onRestore={() => form.restoreValue({ [variableName]: true })}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<></>
|
<NumberInputRoot
|
||||||
{/* <NumberInput.Root
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={form.values[variableName]}
|
value={form.values[variableName]}
|
||||||
onValueChange={(value) => form.setValues({ [variableName]: value })}
|
onValueChange={onEvent}
|
||||||
step={step}
|
step={step}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
>
|
>
|
||||||
<NumberInput.Input />
|
<NumberInputField />
|
||||||
</NumberInput.Root> */}
|
</NumberInputRoot>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormSelect } from '@/components/form/FormSelect';
|
import { FormSelect } from '@/components/form/FormSelect';
|
||||||
import { useFormidable } from '@/components/form/Formidable';
|
import { useFormidable } from '@/components/form/Formidable';
|
||||||
import { Flex } from '@/ui';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/FormSelect',
|
title: 'Components/FormSelect',
|
||||||
@ -94,7 +94,7 @@ export const DarkBackground = {
|
|||||||
render: () => {
|
render: () => {
|
||||||
const form = useFormidable<BasicFormData>({});
|
const form = useFormidable<BasicFormData>({});
|
||||||
return (
|
return (
|
||||||
<Flex style={{ padding: "4", color: "white", background: "gray.800" }}>
|
<Box p="4" color="white" bg="gray.800">
|
||||||
<FormSelect
|
<FormSelect
|
||||||
label="Simple Title for (DarkBackground)"
|
label="Simple Title for (DarkBackground)"
|
||||||
form={form}
|
form={form}
|
||||||
@ -105,7 +105,7 @@ export const DarkBackground = {
|
|||||||
{ id: 333, name: 'third item' },
|
{ id: 333, name: 'third item' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
import { Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormSelectMultiple } from '@/components/form/FormSelectMultiple';
|
import { FormSelectMultiple } from '@/components/form/FormSelectMultiple';
|
||||||
import { useFormidable } from '@/components/form/Formidable';
|
import { useFormidable } from '@/components/form/Formidable';
|
||||||
import { Flex } from '@/ui';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/FormSelectMultipleMultiple',
|
title: 'Components/FormSelectMultipleMultiple',
|
||||||
@ -94,7 +94,7 @@ export const DarkBackground = {
|
|||||||
render: () => {
|
render: () => {
|
||||||
const form = useFormidable<BasicFormData>({});
|
const form = useFormidable<BasicFormData>({});
|
||||||
return (
|
return (
|
||||||
<Flex style={{ padding: "4", color: "white", background: "gray.800" }}>
|
<Box p="4" color="white" bg="gray.800">
|
||||||
<FormSelectMultiple
|
<FormSelectMultiple
|
||||||
label="Simple Title for (DarkBackground)"
|
label="Simple Title for (DarkBackground)"
|
||||||
form={form}
|
form={form}
|
||||||
@ -105,7 +105,7 @@ export const DarkBackground = {
|
|||||||
{ id: 333, name: 'third item' },
|
{ id: 333, name: 'third item' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
|
|
||||||
|
import { Textarea } from '@chakra-ui/react';
|
||||||
|
|
||||||
import { FormGroup } from '@/components/form/FormGroup';
|
import { FormGroup } from '@/components/form/FormGroup';
|
||||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
import { LuDisc3 } from 'react-icons/lu';
|
import { LuDisc3 } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Gender } from '@/back-api';
|
import { Gender } from '@/back-api';
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
import { useCountTracksOfAGender } from '@/service/Track';
|
import { useCountTracksOfAGender } from '@/service/Track';
|
||||||
import { Flex, Span, Text } from '@/ui';
|
|
||||||
|
|
||||||
export type DisplayGenderProps = {
|
export type DisplayGenderProps = {
|
||||||
dataGender?: Gender;
|
dataGender?: Gender;
|
||||||
@ -24,7 +23,7 @@ export const DisplayGender = ({ dataGender }: DisplayGenderProps) => {
|
|||||||
data={dataGender?.covers}
|
data={dataGender?.covers}
|
||||||
size="100"
|
size="100"
|
||||||
height="full"
|
height="full"
|
||||||
//TODO: iconEmpty={LuDisc3}
|
iconEmpty={<LuDisc3 />}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
@ -32,36 +31,31 @@ export const DisplayGender = ({ dataGender }: DisplayGenderProps) => {
|
|||||||
maxWidth="150px"
|
maxWidth="150px"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
style={{
|
overflowX="hidden"
|
||||||
overflowX: "hidden"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Span
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
style={{
|
userSelect="none"
|
||||||
userSelect: "none",
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
overflow="hidden"
|
||||||
overflow: "hidden",
|
|
||||||
alignContent: "left",
|
|
||||||
}}
|
|
||||||
|
|
||||||
//TODO: noOfLines={[1, 2]}
|
//TODO: noOfLines={[1, 2]}
|
||||||
>
|
>
|
||||||
{dataGender?.name}
|
{dataGender?.name}
|
||||||
</Span>
|
</Text>
|
||||||
<Span
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
fontSize="15px"
|
fontSize="15px"
|
||||||
style={{
|
userSelect="none"
|
||||||
userSelect: "none",
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
overflow="hidden"
|
||||||
overflow: "hidden",
|
|
||||||
alignContent: "left",
|
|
||||||
}}
|
|
||||||
//TODO: noOfLines={1}
|
//TODO: noOfLines={1}
|
||||||
>
|
>
|
||||||
{countTracksOnAGender} track{countTracksOnAGender >= 1 && 's'}
|
{countTracksOnAGender} track{countTracksOnAGender >= 1 && 's'}
|
||||||
</Span>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
MdAdminPanelSettings,
|
MdAdminPanelSettings,
|
||||||
MdDeleteForever,
|
MdDeleteForever,
|
||||||
@ -19,7 +24,9 @@ import { useAlbumService, useSpecificAlbum } from '@/service/Album';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { useCountTracksWithAlbumId } from '@/service/Track';
|
import { useCountTracksWithAlbumId } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
|
import { Button } from '../ui/button';
|
||||||
|
|
||||||
|
|
||||||
export type AlbumEditPopUpProps = {};
|
export type AlbumEditPopUpProps = {};
|
||||||
|
|
||||||
@ -128,110 +135,109 @@ export const AlbumEditPopUp = ({ }: AlbumEditPopUpProps) => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return <></>;
|
return (
|
||||||
// return (
|
<DialogRoot
|
||||||
// <Dialog.Root
|
//initialFocusRef={initialRef}
|
||||||
// //initialFocusRef={initialRef}
|
//finalFocusRef={finalRef}
|
||||||
// //finalFocusRef={finalRef}
|
//closeOnOverlayClick={false}
|
||||||
// //closeOnOverlayClick={false}
|
//onOpenChange={onClose}
|
||||||
// onOpenChange={onClose}
|
open={true}
|
||||||
// open={true}
|
data-testid="album-edit-pop-up"
|
||||||
// data-testid="album-edit-pop-up"
|
>
|
||||||
// >
|
{/* <DialogOverlay /> */}
|
||||||
// {/* <DialogOverlay /> */}
|
{/* <DialogCloseTrigger /> */}
|
||||||
// {/* <DialogCloseTrigger /> */}
|
<DialogContent>
|
||||||
// <Dialog.Content>
|
<DialogHeader>Edit Album</DialogHeader>
|
||||||
// <Dialog.Header>Edit Album</Dialog.Header>
|
{/* <DialogCloseButton ref={finalRef} /> */}
|
||||||
// {/* <DialogCloseButton ref={finalRef} /> */}
|
|
||||||
|
|
||||||
// <Dialog.Body pb={6} gap="0px" paddingLeft="18px">
|
<DialogBody pb={6} gap="0px" paddingLeft="18px">
|
||||||
// {admin && (
|
{admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormGroup isRequired label="Id">
|
<FormGroup isRequired label="Id">
|
||||||
// <Text>{dataAlbum?.id}</Text>
|
<Text>{dataAlbum?.id}</Text>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// {countTracksOfAnAlbum !== 0 && (
|
{countTracksOfAnAlbum !== 0 && (
|
||||||
// <Flex paddingLeft="14px">
|
<Flex paddingLeft="14px">
|
||||||
// <MdWarning color="red.600" />
|
<MdWarning color="red.600" />
|
||||||
// <Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
<Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
||||||
// Can not remove album {countTracksOfAnAlbum} track(s) depend
|
Can not remove album {countTracksOfAnAlbum} track(s) depend
|
||||||
// on it.
|
on it.
|
||||||
// </Text>
|
</Text>
|
||||||
// </Flex>
|
</Flex>
|
||||||
// )}
|
)}
|
||||||
// <FormGroup label="Action(s):">
|
<FormGroup label="Action(s):">
|
||||||
// <Button
|
<Button
|
||||||
// onClick={disclosure.onOpen}
|
onClick={disclosure.onOpen}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// theme="@danger"
|
colorPalette="@danger"
|
||||||
// disabled={countTracksOfAnAlbum !== 0}
|
disabled={countTracksOfAnAlbum !== 0}
|
||||||
// >
|
>
|
||||||
// <MdDeleteForever /> Remove Media
|
<MdDeleteForever /> Remove Media
|
||||||
// </Button>
|
</Button>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <ConfirmPopUp
|
<ConfirmPopUp
|
||||||
// disclosure={disclosure}
|
disclosure={disclosure}
|
||||||
// title="Remove album"
|
title="Remove album"
|
||||||
// body={`Remove Album [${dataAlbum?.id}] ${dataAlbum?.name}`}
|
body={`Remove Album [${dataAlbum?.id}] ${dataAlbum?.name}`}
|
||||||
// confirmTitle="Remove"
|
confirmTitle="Remove"
|
||||||
// onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// {!admin && (
|
{!admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="name"
|
variableName="name"
|
||||||
// isRequired
|
isRequired
|
||||||
// label="Title"
|
label="Title"
|
||||||
// ref={initialRef}
|
ref={initialRef}
|
||||||
// />
|
/>
|
||||||
// <FormTextarea
|
<FormTextarea
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="description"
|
variableName="description"
|
||||||
// label="Description"
|
label="Description"
|
||||||
// />
|
/>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="publication"
|
variableName="publication"
|
||||||
// label="Publication"
|
label="Publication"
|
||||||
// />
|
/>
|
||||||
// <FormCovers
|
<FormCovers
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="covers"
|
variableName="covers"
|
||||||
// onFilesSelected={onFilesSelected}
|
onFilesSelected={onFilesSelected}
|
||||||
// onUriSelected={onUriSelected}
|
onUriSelected={onUriSelected}
|
||||||
// onRemove={onRemoveCover}
|
onRemove={onRemoveCover}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Dialog.Body>
|
</DialogBody>
|
||||||
// <Dialog.Footer>
|
<DialogFooter>
|
||||||
// <Button
|
<Button
|
||||||
// onClick={() => setAdmin((value) => !value)}
|
onClick={() => setAdmin((value) => !value)}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// >
|
>
|
||||||
// {admin ? (
|
{admin ? (
|
||||||
// <>
|
<>
|
||||||
// <MdEdit />
|
<MdEdit />
|
||||||
// Edit
|
Edit
|
||||||
// </>
|
</>
|
||||||
// ) : (
|
) : (
|
||||||
// <>
|
<>
|
||||||
// <MdAdminPanelSettings />
|
<MdAdminPanelSettings />
|
||||||
// Admin
|
Admin
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Button>
|
</Button>
|
||||||
// {!admin && form.isFormModified && (
|
{!admin && form.isFormModified && (
|
||||||
// <Button colorScheme="blue" mr={3} onClick={onSave}>
|
<Button colorScheme="blue" mr={3} onClick={onSave}>
|
||||||
// Save
|
Save
|
||||||
// </Button>
|
</Button>
|
||||||
// )}
|
)}
|
||||||
// <Button onClick={onClose}>Cancel</Button>
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
// </Dialog.Footer>
|
</DialogFooter>
|
||||||
// </Dialog.Content>
|
</DialogContent>
|
||||||
// </Dialog.Root>
|
</DialogRoot>
|
||||||
// );
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
Button,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
import {
|
import {
|
||||||
MdAdminPanelSettings,
|
MdAdminPanelSettings,
|
||||||
MdDeleteForever,
|
MdDeleteForever,
|
||||||
@ -20,7 +26,6 @@ import { useArtistService, useSpecificArtist } from '@/service/Artist';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { useCountTracksOfAnArtist } from '@/service/Track';
|
import { useCountTracksOfAnArtist } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
|
||||||
|
|
||||||
export type ArtistEditPopUpProps = {};
|
export type ArtistEditPopUpProps = {};
|
||||||
|
|
||||||
@ -129,110 +134,111 @@ export const ArtistEditPopUp = ({ }: ArtistEditPopUpProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
// <Dialog.Root
|
<DialogRoot
|
||||||
// //initialFocusRef={initialRef}
|
//initialFocusRef={initialRef}
|
||||||
// //finalFocusRef={finalRef}
|
//finalFocusRef={finalRef}
|
||||||
// //closeOnOverlayClick={false}
|
//closeOnOverlayClick={false}
|
||||||
// onOpenChange={onClose}
|
onOpenChange={onClose}
|
||||||
// open={true}
|
open={true}
|
||||||
// data-testid="artist-edit-pop-up"
|
data-testid="artist-edit-pop-up"
|
||||||
// >
|
>
|
||||||
// {/* <DialogOverlay /> */}
|
{/* <DialogOverlay /> */}
|
||||||
// <Dialog.Content>
|
<DialogContent>
|
||||||
// <Dialog.Header>Edit Artist</Dialog.Header>
|
<DialogHeader>Edit Artist</DialogHeader>
|
||||||
// {/* <DialogCloseButton ref={finalRef} /> */}
|
{/* <DialogCloseButton ref={finalRef} /> */}
|
||||||
|
|
||||||
// <Dialog.Body pb={6} gap="0px" paddingLeft="18px">
|
<DialogBody pb={6} gap="0px" paddingLeft="18px">
|
||||||
// {admin && (
|
{admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormGroup isRequired label="Id">
|
<FormGroup isRequired label="Id">
|
||||||
// <Text>{dataArtist?.id}</Text>
|
<Text>{dataArtist?.id}</Text>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// {countTracksOnAnArtist !== 0 && (
|
{countTracksOnAnArtist !== 0 && (
|
||||||
// <Flex paddingLeft="14px">
|
<Flex paddingLeft="14px">
|
||||||
// <MdWarning color="red.600" />
|
<MdWarning color="red.600" />
|
||||||
// <Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
<Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
||||||
// Can not remove artist {countTracksOnAnArtist} track(s)
|
Can not remove artist {countTracksOnAnArtist} track(s)
|
||||||
// depend on it.
|
depend on it.
|
||||||
// </Text>
|
</Text>
|
||||||
// </Flex>
|
</Flex>
|
||||||
// )}
|
)}
|
||||||
// <FormGroup label="Action(s):">
|
<FormGroup label="Action(s):">
|
||||||
// <Button
|
<Button
|
||||||
// onClick={disclosure.onOpen}
|
onClick={disclosure.onOpen}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// theme="@danger"
|
colorPalette="@danger"
|
||||||
// disabled={countTracksOnAnArtist !== 0}
|
disabled={countTracksOnAnArtist !== 0}
|
||||||
// >
|
>
|
||||||
// <MdDeleteForever /> Remove Media
|
<MdDeleteForever /> Remove Media
|
||||||
// </Button>
|
</Button>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <ConfirmPopUp
|
<ConfirmPopUp
|
||||||
// disclosure={disclosure}
|
disclosure={disclosure}
|
||||||
// title="Remove artist"
|
title="Remove artist"
|
||||||
// body={`Remove Artist [${dataArtist?.id}] ${dataArtist?.name}`}
|
body={`Remove Artist [${dataArtist?.id}] ${dataArtist?.name}`}
|
||||||
// confirmTitle="Remove"
|
confirmTitle="Remove"
|
||||||
// onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// {!admin && (
|
{!admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="name"
|
variableName="name"
|
||||||
// isRequired
|
isRequired
|
||||||
// label="Artist name"
|
label="Artist name"
|
||||||
// ref={initialRef}
|
ref={initialRef}
|
||||||
// />
|
/>
|
||||||
// <FormTextarea
|
<FormTextarea
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="description"
|
variableName="description"
|
||||||
// label="Description"
|
label="Description"
|
||||||
// />
|
/>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="firstName"
|
variableName="firstName"
|
||||||
// label="First Name"
|
label="First Name"
|
||||||
// />
|
/>
|
||||||
// <FormInput form={form} variableName="surname" label="SurName" />
|
<FormInput form={form} variableName="surname" label="SurName" />
|
||||||
// <FormInput form={form} variableName="birth" label="Birth date" />
|
<FormInput form={form} variableName="birth" label="Birth date" />
|
||||||
// <FormInput form={form} variableName="death" label="Death date" />
|
<FormInput form={form} variableName="death" label="Death date" />
|
||||||
// <FormCovers
|
<FormCovers
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="covers"
|
variableName="covers"
|
||||||
// onFilesSelected={onFilesSelected}
|
onFilesSelected={onFilesSelected}
|
||||||
// onUriSelected={onUriSelected}
|
onUriSelected={onUriSelected}
|
||||||
// onRemove={onRemoveCover}
|
onRemove={onRemoveCover}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Dialog.Body>
|
</DialogBody>
|
||||||
// <Dialog.Footer>
|
<DialogFooter>
|
||||||
// <Button
|
<Button
|
||||||
// onClick={() => setAdmin((value) => !value)}
|
onClick={() => setAdmin((value) => !value)}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// >
|
colorPalette={admin ? undefined : "@danger"}
|
||||||
// {admin ? (
|
>
|
||||||
// <>
|
{admin ? (
|
||||||
// <MdEdit />
|
<>
|
||||||
// Edit
|
<MdEdit />
|
||||||
// </>
|
Edit
|
||||||
// ) : (
|
</>
|
||||||
// <>
|
) : (
|
||||||
// <MdAdminPanelSettings />
|
<>
|
||||||
// Admin
|
<MdAdminPanelSettings />
|
||||||
// </>
|
Admin
|
||||||
// )}
|
</>
|
||||||
// </Button>
|
)}
|
||||||
// {!admin && form.isFormModified && (
|
</Button>
|
||||||
// <Button colorScheme="blue" mr={3} onClick={onSave}>
|
{!admin && form.isFormModified && (
|
||||||
// Save
|
<Button colorScheme="blue" mr={3} onClick={onSave}>
|
||||||
// </Button>
|
Save
|
||||||
// )}
|
</Button>
|
||||||
// <Button onClick={onClose}>Cancel</Button>
|
)}
|
||||||
// </Dialog.Footer>
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
// </Dialog.Content>
|
</DialogFooter>
|
||||||
// </Dialog.Root>
|
</DialogContent>
|
||||||
<></>);
|
</DialogRoot>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { UseDisclosureReturn } from '@/utils/disclosure';
|
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
UseDisclosureReturn,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
export type ConfirmPopUpProps = {
|
export type ConfirmPopUpProps = {
|
||||||
title: string;
|
title: string;
|
||||||
body: string;
|
body: string;
|
||||||
@ -23,28 +26,29 @@ export const ConfirmPopUp = ({
|
|||||||
disclosure.onClose();
|
disclosure.onClose();
|
||||||
};
|
};
|
||||||
const cancelRef = useRef<HTMLButtonElement>(null);
|
const cancelRef = useRef<HTMLButtonElement>(null);
|
||||||
return <></>;
|
return (
|
||||||
// return (
|
<DialogRoot role="alertdialog"
|
||||||
// <Dialog.Root role="alertdialog"
|
open={disclosure.open}
|
||||||
// open={disclosure.open}
|
//leastDestructiveRef={cancelRef}
|
||||||
// //leastDestructiveRef={cancelRef}
|
onOpenChange={disclosure.onClose}
|
||||||
// onOpenChange={disclosure.onClose}
|
data-testid="confirm-pop-up"
|
||||||
// data-testid="confirm-pop-up"
|
>
|
||||||
// >
|
<DialogContent>
|
||||||
// <Dialog.Content>
|
<DialogHeader fontSize="lg" fontWeight="bold">
|
||||||
// <Dialog.Header fontSize="lg" fontWeight="bold">
|
{title}
|
||||||
// {title}
|
</DialogHeader>
|
||||||
// </Dialog.Header>
|
|
||||||
// <Dialog.Body>{body}</Dialog.Body>
|
<DialogBody>{body}</DialogBody>
|
||||||
// <Dialog.Footer>
|
|
||||||
// <Button onClick={disclosure.onClose} ref={cancelRef}>
|
<DialogFooter>
|
||||||
// Cancel
|
<Button onClick={disclosure.onClose} ref={cancelRef}>
|
||||||
// </Button>
|
Cancel
|
||||||
// <Button colorScheme="red" onClick={onClickConfirm} ml={3}>
|
</Button>
|
||||||
// {confirmTitle}
|
<Button colorScheme="red" onClick={onClickConfirm} ml={3}>
|
||||||
// </Button>
|
{confirmTitle}
|
||||||
// </Dialog.Footer>
|
</Button>
|
||||||
// </Dialog.Content>
|
</DialogFooter>
|
||||||
// </Dialog.Root>
|
</DialogContent>
|
||||||
// );
|
</DialogRoot>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
Button
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
MdAdminPanelSettings,
|
MdAdminPanelSettings,
|
||||||
MdDeleteForever,
|
MdDeleteForever,
|
||||||
@ -8,6 +13,7 @@ import {
|
|||||||
MdWarning,
|
MdWarning,
|
||||||
} from 'react-icons/md';
|
} from 'react-icons/md';
|
||||||
|
|
||||||
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Gender, GenderResource } from '@/back-api';
|
import { Gender, GenderResource } from '@/back-api';
|
||||||
@ -21,7 +27,6 @@ import { useGenderService, useSpecificGender } from '@/service/Gender';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { useCountTracksOfAGender } from '@/service/Track';
|
import { useCountTracksOfAGender } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
|
||||||
|
|
||||||
export type GenderEditPopUpProps = {};
|
export type GenderEditPopUpProps = {};
|
||||||
|
|
||||||
@ -128,104 +133,103 @@ export const GenderEditPopUp = ({ }: GenderEditPopUpProps) => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return <></>;
|
return (
|
||||||
// return (
|
<DialogRoot
|
||||||
// <Dialog.Root
|
//initialFocusRef={initialRef}
|
||||||
// //initialFocusRef={initialRef}
|
//finalFocusRef={finalRef}
|
||||||
// //finalFocusRef={finalRef}
|
//closeOnOverlayClick={false}
|
||||||
// //closeOnOverlayClick={false}
|
onOpenChange={onClose}
|
||||||
// onOpenChange={onClose}
|
open={true}
|
||||||
// open={true}
|
data-testid="gender-edit-pop-up"
|
||||||
// data-testid="gender-edit-pop-up"
|
>
|
||||||
// >
|
{/* <DialogOverlay /> */}
|
||||||
// {/* <DialogOverlay /> */}
|
<DialogContent>
|
||||||
// <Dialog.Content>
|
<DialogHeader>Edit Gender</DialogHeader>
|
||||||
// <Dialog.Header>Edit Gender</Dialog.Header>
|
{/* <DialogCloseButton ref={finalRef} /> */}
|
||||||
// {/* <DialogCloseButton ref={finalRef} /> */}
|
|
||||||
|
|
||||||
// <Dialog.Body pb={6} gap="0px" paddingLeft="18px">
|
<DialogBody pb={6} gap="0px" paddingLeft="18px">
|
||||||
// {admin && (
|
{admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormGroup isRequired label="Id">
|
<FormGroup isRequired label="Id">
|
||||||
// <Text>{dataGender?.id}</Text>
|
<Text>{dataGender?.id}</Text>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// {countTracksOnAGender !== 0 && (
|
{countTracksOnAGender !== 0 && (
|
||||||
// <Flex paddingLeft="14px">
|
<Flex paddingLeft="14px">
|
||||||
// <MdWarning color="red.600" />
|
<MdWarning color="red.600" />
|
||||||
// <Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
<Text paddingLeft="6px" color="red.600" fontWeight="bold">
|
||||||
// Can not remove gender {countTracksOnAGender} track(s) depend
|
Can not remove gender {countTracksOnAGender} track(s) depend
|
||||||
// on it.
|
on it.
|
||||||
// </Text>
|
</Text>
|
||||||
// </Flex>
|
</Flex>
|
||||||
// )}
|
)}
|
||||||
// <FormGroup label="Action(s):">
|
<FormGroup label="Action(s):">
|
||||||
// <Button
|
<Button
|
||||||
// onClick={disclosure.onOpen}
|
onClick={disclosure.onOpen}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// theme="@danger"
|
colorPalette="@danger"
|
||||||
// disabled={countTracksOnAGender !== 0}
|
disabled={countTracksOnAGender !== 0}
|
||||||
// >
|
>
|
||||||
// <MdDeleteForever /> Remove gender
|
<MdDeleteForever /> Remove gender
|
||||||
// </Button>
|
</Button>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <ConfirmPopUp
|
<ConfirmPopUp
|
||||||
// disclosure={disclosure}
|
disclosure={disclosure}
|
||||||
// title="Remove gender"
|
title="Remove gender"
|
||||||
// body={`Remove gender [${dataGender?.id}] ${dataGender?.name}`}
|
body={`Remove gender [${dataGender?.id}] ${dataGender?.name}`}
|
||||||
// confirmTitle="Remove"
|
confirmTitle="Remove"
|
||||||
// onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// {!admin && (
|
{!admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="name"
|
variableName="name"
|
||||||
// isRequired
|
isRequired
|
||||||
// label="Gender name"
|
label="Gender name"
|
||||||
// ref={initialRef}
|
ref={initialRef}
|
||||||
// />
|
/>
|
||||||
// <FormTextarea
|
<FormTextarea
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="description"
|
variableName="description"
|
||||||
// label="Description"
|
label="Description"
|
||||||
// />
|
/>
|
||||||
// <FormCovers
|
<FormCovers
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="covers"
|
variableName="covers"
|
||||||
// onFilesSelected={onFilesSelected}
|
onFilesSelected={onFilesSelected}
|
||||||
// onUriSelected={onUriSelected}
|
onUriSelected={onUriSelected}
|
||||||
// onRemove={onRemoveCover}
|
onRemove={onRemoveCover}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Dialog.Body>
|
</DialogBody>
|
||||||
// <Dialog.Footer>
|
<DialogFooter>
|
||||||
// <Button
|
<Button
|
||||||
// onClick={() => setAdmin((value) => !value)}
|
onClick={() => setAdmin((value) => !value)}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// >
|
>
|
||||||
// {admin ? (
|
{admin ? (
|
||||||
// <>
|
<>
|
||||||
// <MdEdit />
|
<MdEdit />
|
||||||
// Edit
|
Edit
|
||||||
// </>
|
</>
|
||||||
// ) : (
|
) : (
|
||||||
// <>
|
<>
|
||||||
// <MdAdminPanelSettings />
|
<MdAdminPanelSettings />
|
||||||
// Admin
|
Admin
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Button>
|
</Button>
|
||||||
// {!admin && form.isFormModified && (
|
{!admin && form.isFormModified && (
|
||||||
// <Button colorScheme="blue" mr={3} onClick={onSave}>
|
<Button colorScheme="blue" mr={3} onClick={onSave}>
|
||||||
// Save
|
Save
|
||||||
// </Button>
|
</Button>
|
||||||
// )}
|
)}
|
||||||
// <Button onClick={onClose}>Cancel</Button>
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
// </Dialog.Footer>
|
</DialogFooter>
|
||||||
// </Dialog.Content>
|
</DialogContent>
|
||||||
// </Dialog.Root>
|
</DialogRoot>
|
||||||
// );
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Progress,
|
||||||
|
Text,
|
||||||
|
Button,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
|
|
||||||
|
|
||||||
export type PopUpUploadProgressProps = {
|
export type PopUpUploadProgressProps = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -32,66 +41,64 @@ export const PopUpUploadProgress = ({
|
|||||||
}: PopUpUploadProgressProps) => {
|
}: PopUpUploadProgressProps) => {
|
||||||
const initialRef = useRef<HTMLButtonElement>(null);
|
const initialRef = useRef<HTMLButtonElement>(null);
|
||||||
const finalRef = useRef<HTMLButtonElement>(null);
|
const finalRef = useRef<HTMLButtonElement>(null);
|
||||||
return <></>;
|
return (
|
||||||
|
<DialogRoot
|
||||||
|
//initialFocusRef={initialRef}
|
||||||
|
//finalFocusRef={finalRef}
|
||||||
|
//closeOnOverlayClick={false}
|
||||||
|
onOpenChange={onClose}
|
||||||
|
open={true}
|
||||||
|
data-testid="upload-progress-edit-pop-up"
|
||||||
|
>
|
||||||
|
{/* <DialogOverlay /> */}
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>{title}</DialogHeader>
|
||||||
|
{/* <DialogCloseButton ref={finalRef} /> */}
|
||||||
|
|
||||||
// return (
|
<DialogBody pb={6} paddingLeft="18px">
|
||||||
// <Dialog.Root
|
<Flex direction="column" gap="10px">
|
||||||
// //initialFocusRef={initialRef}
|
{isFinished ? (
|
||||||
// //finalFocusRef={finalRef}
|
<Text fontSize="20px" fontWeight="bold">
|
||||||
// //closeOnOverlayClick={false}
|
All {elements.length} element have been sent
|
||||||
// onOpenChange={onClose}
|
</Text>
|
||||||
// open={true}
|
) : (
|
||||||
// data-testid="upload-progress-edit-pop-up"
|
<Text fontSize="20px" fontWeight="bold">
|
||||||
// >
|
[{index + 1}/{elements.length}] {elements[index]}
|
||||||
// {/* <DialogOverlay /> */}
|
</Text>
|
||||||
// <Dialog.Content>
|
)}
|
||||||
// <Dialog.Header>{title}</Dialog.Header>
|
<Progress.Root
|
||||||
// {/* <DialogCloseButton ref={finalRef} /> */}
|
colorScheme="green"
|
||||||
|
striped
|
||||||
// <Dialog.Body pb={6} paddingLeft="18px">
|
value={currentSize}
|
||||||
// <Flex direction="column" gap="10px">
|
animated
|
||||||
// {isFinished ? (
|
max={totalSize}
|
||||||
// <Text fontSize="20px" fontWeight="bold">
|
height="24px"
|
||||||
// All {elements.length} element have been sent
|
/>
|
||||||
// </Text>
|
<Flex>
|
||||||
// ) : (
|
<Text>{currentSize.toLocaleString('fr-FR')} Bytes</Text>
|
||||||
// <Text fontSize="20px" fontWeight="bold">
|
<Text marginLeft="auto">
|
||||||
// [{index + 1}/{elements.length}] {elements[index]}
|
{totalSize.toLocaleString('fr-FR')} Bytes
|
||||||
// </Text>
|
</Text>
|
||||||
// )}
|
</Flex>
|
||||||
// <Progress.Root
|
{error && (
|
||||||
// colorScheme="green"
|
<Text fontWeight="bold" color="darkred">
|
||||||
// striped
|
{error}
|
||||||
// value={currentSize}
|
</Text>
|
||||||
// animated
|
)}
|
||||||
// max={totalSize}
|
</Flex>
|
||||||
// height="24px"
|
</DialogBody>
|
||||||
// />
|
<DialogFooter>
|
||||||
// <Flex>
|
{isFinished ? (
|
||||||
// <Text>{currentSize.toLocaleString('fr-FR')} Bytes</Text>
|
<Button onClick={onClose} colorPalette="green">
|
||||||
// <Text marginLeft="auto">
|
Ok
|
||||||
// {totalSize.toLocaleString('fr-FR')} Bytes
|
</Button>
|
||||||
// </Text>
|
) : (
|
||||||
// </Flex>
|
<Button colorScheme="red" mr={3} onClick={onAbort} ref={initialRef}>
|
||||||
// {error && (
|
Abort
|
||||||
// <Text fontWeight="bold" color="darkred">
|
</Button>
|
||||||
// {error}
|
)}
|
||||||
// </Text>
|
</DialogFooter>
|
||||||
// )}
|
</DialogContent>
|
||||||
// </Flex>
|
</DialogRoot>
|
||||||
// </Dialog.Body>
|
);
|
||||||
// <Dialog.Footer>
|
|
||||||
// {isFinished ? (
|
|
||||||
// <Button onClick={onClose} theme="@success">
|
|
||||||
// Ok
|
|
||||||
// </Button>
|
|
||||||
// ) : (
|
|
||||||
// <Button colorScheme="red" mr={3} onClick={onAbort} ref={initialRef}>
|
|
||||||
// Abort
|
|
||||||
// </Button>
|
|
||||||
// )}
|
|
||||||
// </Dialog.Footer>
|
|
||||||
// </Dialog.Content>
|
|
||||||
// </Dialog.Root>
|
|
||||||
// );
|
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Text,
|
||||||
|
useDisclosure,
|
||||||
|
Button,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||||
|
|
||||||
import { MdAdminPanelSettings, MdDeleteForever, MdEdit } from 'react-icons/md';
|
import { MdAdminPanelSettings, MdDeleteForever, MdEdit } from 'react-icons/md';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
@ -19,7 +26,6 @@ import { useOrderedGenders } from '@/service/Gender';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { useSpecificTrack, useTrackService } from '@/service/Track';
|
import { useSpecificTrack, useTrackService } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
|
||||||
|
|
||||||
export type TrackEditPopUpProps = {};
|
export type TrackEditPopUpProps = {};
|
||||||
|
|
||||||
@ -80,117 +86,116 @@ export const TrackEditPopUp = ({ }: TrackEditPopUpProps) => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return <></>;
|
return (
|
||||||
// return (
|
<DialogRoot
|
||||||
// <Dialog.Root
|
//initialFocusRef={initialRef}
|
||||||
// //initialFocusRef={initialRef}
|
//finalFocusRef={finalRef}
|
||||||
// //finalFocusRef={finalRef}
|
//closeOnOverlayClick={false}
|
||||||
// //closeOnOverlayClick={false}
|
onOpenChange={onClose}
|
||||||
// onOpenChange={onClose}
|
open={true}
|
||||||
// open={true}
|
data-testid="track-edit-pop-up"
|
||||||
// data-testid="track-edit-pop-up"
|
>
|
||||||
// >
|
{/* <DialogOverlay /> */}
|
||||||
// {/* <DialogOverlay /> */}
|
<DialogContent>
|
||||||
// <Dialog.Content>
|
<DialogHeader>Edit Track</DialogHeader>
|
||||||
// <Dialog.Header>Edit Track</Dialog.Header>
|
{/* <DialogCloseButton ref={finalRef} /> */}
|
||||||
// {/* <DialogCloseButton ref={finalRef} /> */}
|
|
||||||
|
|
||||||
// <Dialog.Body pb={6} gap="0px" paddingLeft="18px">
|
<DialogBody pb={6} gap="0px" paddingLeft="18px">
|
||||||
// {admin && (
|
{admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormGroup isRequired label="Id">
|
<FormGroup isRequired label="Id">
|
||||||
// <Text>{dataTrack?.id}</Text>
|
<Text>{dataTrack?.id}</Text>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <FormGroup label="Data Id">
|
<FormGroup label="Data Id">
|
||||||
// <Text>{dataTrack?.dataId}</Text>
|
<Text>{dataTrack?.dataId}</Text>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <FormGroup label="Action(s):">
|
<FormGroup label="Action(s):">
|
||||||
// <Button
|
<Button
|
||||||
// onClick={disclosure.onOpen}
|
onClick={disclosure.onOpen}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// theme="@danger"
|
colorPalette="@danger"
|
||||||
// >
|
>
|
||||||
// <MdDeleteForever /> Remove Media
|
<MdDeleteForever /> Remove Media
|
||||||
// </Button>
|
</Button>
|
||||||
// </FormGroup>
|
</FormGroup>
|
||||||
// <ConfirmPopUp
|
<ConfirmPopUp
|
||||||
// disclosure={disclosure}
|
disclosure={disclosure}
|
||||||
// title="Remove track"
|
title="Remove track"
|
||||||
// body={`Remove Media [${dataTrack?.id}] ${dataTrack?.name}`}
|
body={`Remove Media [${dataTrack?.id}] ${dataTrack?.name}`}
|
||||||
// confirmTitle="Remove"
|
confirmTitle="Remove"
|
||||||
// onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// {!admin && (
|
{!admin && (
|
||||||
// <>
|
<>
|
||||||
// <FormInput
|
<FormInput
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="name"
|
variableName="name"
|
||||||
// isRequired
|
isRequired
|
||||||
// label="Title"
|
label="Title"
|
||||||
// ref={initialRef}
|
ref={initialRef}
|
||||||
// />
|
/>
|
||||||
// <FormTextarea
|
<FormTextarea
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="description"
|
variableName="description"
|
||||||
// label="Description"
|
label="Description"
|
||||||
// />
|
/>
|
||||||
// <FormSelect
|
<FormSelect
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="genderId"
|
variableName="genderId"
|
||||||
// options={dataGenders}
|
options={dataGenders}
|
||||||
// label="Gender"
|
label="Gender"
|
||||||
// />
|
/>
|
||||||
// <FormSelectMultiple
|
<FormSelectMultiple
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="artists"
|
variableName="artists"
|
||||||
// options={dataArtist}
|
options={dataArtist}
|
||||||
// label="Artist(s)"
|
label="Artist(s)"
|
||||||
// />
|
/>
|
||||||
// <FormSelect
|
<FormSelect
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="albumId"
|
variableName="albumId"
|
||||||
// options={dataAlbums}
|
options={dataAlbums}
|
||||||
// label="Album"
|
label="Album"
|
||||||
// />
|
/>
|
||||||
// <FormNumber
|
<FormNumber
|
||||||
// form={form}
|
form={form}
|
||||||
// variableName="track"
|
variableName="track"
|
||||||
// label="Track n°"
|
label="Track n°"
|
||||||
// step={1}
|
step={1}
|
||||||
// //defaultValue={0}
|
//defaultValue={0}
|
||||||
// min={0}
|
min={0}
|
||||||
// max={1000}
|
max={1000}
|
||||||
// />
|
/>
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Dialog.Body>
|
</DialogBody>
|
||||||
// <Dialog.Footer>
|
<DialogFooter>
|
||||||
// <Button
|
<Button
|
||||||
// onClick={() => setAdmin((value) => !value)}
|
onClick={() => setAdmin((value) => !value)}
|
||||||
// marginRight="auto"
|
marginRight="auto"
|
||||||
// >
|
>
|
||||||
// {admin ? (
|
{admin ? (
|
||||||
// <>
|
<>
|
||||||
// <MdEdit />
|
<MdEdit />
|
||||||
// Edit
|
Edit
|
||||||
// </>
|
</>
|
||||||
// ) : (
|
) : (
|
||||||
// <>
|
<>
|
||||||
// <MdAdminPanelSettings />
|
<MdAdminPanelSettings />
|
||||||
// Admin
|
Admin
|
||||||
// </>
|
</>
|
||||||
// )}
|
)}
|
||||||
// </Button>
|
</Button>
|
||||||
// {!admin && form.isFormModified && (
|
{!admin && form.isFormModified && (
|
||||||
// <Button colorScheme="blue" mr={3} onClick={onSave}>
|
<Button colorScheme="blue" mr={3} onClick={onSave}>
|
||||||
// Save
|
Save
|
||||||
// </Button>
|
</Button>
|
||||||
// )}
|
)}
|
||||||
// <Button onClick={onClose}>Cancel</Button>
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
// </Dialog.Footer>
|
</DialogFooter>
|
||||||
// </Dialog.Content>
|
</DialogContent>
|
||||||
// </Dialog.Root>
|
</DialogRoot>
|
||||||
// );
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||||
import { MdAdd } from 'react-icons/md';
|
import { MdAdd } from 'react-icons/md';
|
||||||
|
|
||||||
import { isNullOrUndefined, isNumber } from '@/utils/validator';
|
import { isNullOrUndefined, isNumber } from '@/utils/validator';
|
||||||
import { Button, Flex, Text } from '@/ui';
|
|
||||||
|
|
||||||
export type SelectListModel = {
|
export type SelectListModel = {
|
||||||
id: any;
|
id: any;
|
||||||
@ -65,65 +65,56 @@ export const SelectList = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Flex style={{ position: "relative" }}>
|
<Box position="relative">
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
style={{
|
width="full"
|
||||||
width: "full",
|
position="absolute"
|
||||||
position: "absolute",
|
border="1px"
|
||||||
border: "1px",
|
borderColor="black"
|
||||||
borderColor: "black",
|
backgroundColor="gray.700"
|
||||||
backgroundColor: "gray.700",
|
overflowY="auto"
|
||||||
overflowY: "auto",
|
overflowX="hidden"
|
||||||
overflowX: "hidden",
|
maxHeight="300px"
|
||||||
maxHeight: "300px",
|
zIndex={300}
|
||||||
zIndex: 300,
|
transform="translateY(1px)"
|
||||||
transform: "translateY(1px)",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{displayedValue.length === 0 && (
|
{displayedValue.length === 0 && (
|
||||||
<Text fontWeight="bold" color="red.500" style={{ margin: "0 auto" }}>
|
<Text marginX="auto" color="red.500" fontWeight="bold" marginY="10px">
|
||||||
... No element found...
|
... No element found...
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{displayedValue.map((data) => (
|
{displayedValue.map((data) => (
|
||||||
<Button
|
<Button
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
marginY="1px"
|
||||||
margin: "1px 0",
|
borderRadius="0px"
|
||||||
borderRadius: "0px",
|
autoFocus={false}
|
||||||
//autoFocus: false,
|
backgroundColor={data.isSelected ? 'green.800' : '0x00000000'}
|
||||||
backgroundColor: data.isSelected ? 'green.800' : '0x00000000',
|
_hover={{ backgroundColor: 'gray.400' }}
|
||||||
//_hover={ backgroundColor: 'gray.400' },
|
|
||||||
}}
|
|
||||||
onClick={() => onSelectValue(data)}
|
onClick={() => onSelectValue(data)}
|
||||||
//ref={data.isSelected ? scrollToRef : undefined}
|
ref={data.isSelected ? scrollToRef : undefined}
|
||||||
>
|
>
|
||||||
<Text style={{ margin: "0 auto", }} /*autoFocus={false}*/>
|
<Text marginRight="auto" autoFocus={false}>
|
||||||
{data.name}
|
{data.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
{onCreate && search && search.length > 0 && (
|
{onCreate && search && search.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
style={{
|
marginY="1px"
|
||||||
margin: "1px 0",
|
borderRadius="0px"
|
||||||
borderRadius: "0px",
|
autoFocus={false}
|
||||||
//autoFocus:false,
|
_hover={{ backgroundColor: 'gray.400' }}
|
||||||
//_hover={ backgroundColor: 'gray.400' }
|
|
||||||
}}
|
|
||||||
onClick={() => onCreate(search)}
|
onClick={() => onCreate(search)}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex marginRight="auto">
|
||||||
style={{
|
|
||||||
margin: "0 auto",
|
|
||||||
}}>
|
|
||||||
<MdAdd />
|
<MdAdd />
|
||||||
<Text /*autoFocus={false}*/>Create '{search}'</Text>
|
<Text autoFocus={false}>Create '{search}'</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
|
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
HStack,
|
||||||
|
Input,
|
||||||
|
Spinner,
|
||||||
|
Tag,
|
||||||
|
TagLabel,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { MdEdit, MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
|
import { MdEdit, MdKeyboardArrowDown, MdKeyboardArrowUp } from 'react-icons/md';
|
||||||
|
|
||||||
import { SelectList, SelectListModel } from '@/components/select/SelectList';
|
import { SelectList, SelectListModel } from '@/components/select/SelectList';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { Button, Flex, HStack, Input } from '@/ui';
|
|
||||||
|
|
||||||
export type SelectMultipleProps = {
|
export type SelectMultipleProps = {
|
||||||
options?: object[];
|
options?: object[];
|
||||||
@ -75,7 +83,7 @@ export const SelectMultiple = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!options) {
|
if (!options) {
|
||||||
return <></>;// TODO: <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
const onChangeInput = (value: string): void => {
|
const onChangeInput = (value: string): void => {
|
||||||
setHasSuggestion(false);
|
setHasSuggestion(false);
|
||||||
@ -101,19 +109,18 @@ export const SelectMultiple = ({
|
|||||||
return (
|
return (
|
||||||
<Flex direction="column" width="full" gap="0px">
|
<Flex direction="column" width="full" gap="0px">
|
||||||
{selectedOptions && (
|
{selectedOptions && (
|
||||||
<HStack style={{ flexWrap: "wrap", /*spacing="5px"*/ justifyContent: "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
|
||||||
size="md"
|
size="xl"
|
||||||
key="md"
|
|
||||||
borderRadius="5px"
|
borderRadius="5px"
|
||||||
variant="solid"
|
variant="surface"
|
||||||
backgroundColor="green.500"
|
backgroundColor="green.800"
|
||||||
>
|
>
|
||||||
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
|
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
|
||||||
<Tag.CloseTrigger onClick={() => selectValue(data)} />
|
<Tag.CloseTrigger boxSize="5" onClick={() => selectValue(data)} />
|
||||||
</Tag.Root> */}
|
</Tag.Root>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</HStack>
|
</HStack>
|
||||||
@ -122,24 +129,19 @@ export const SelectMultiple = ({
|
|||||||
<Flex>
|
<Flex>
|
||||||
<Input
|
<Input
|
||||||
ref={refFocus}
|
ref={refFocus}
|
||||||
style={{
|
width="full"
|
||||||
width: "full",
|
|
||||||
borderRadius: "5px 0 0 5px",
|
|
||||||
}}
|
|
||||||
onChange={(e) => onChangeInput(e.target.value)}
|
onChange={(e) => onChangeInput(e.target.value)}
|
||||||
//onSubmit={onSubmit}
|
//onSubmit={onSubmit}
|
||||||
// TODO: onFocus={() => setShowList(true)}
|
onFocus={() => setShowList(true)}
|
||||||
// TODO: 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"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={onOpenClose}
|
onClick={onOpenClose}
|
||||||
//variant="outline"
|
variant="outline"
|
||||||
style={{
|
borderRadius="0 5px 5px 0"
|
||||||
borderRadius: "0 5px 5px 0",
|
borderWidth="1px 1px 1px 0"
|
||||||
borderWidth: "1px 1px 1px 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{showList ? (
|
{showList ? (
|
||||||
<MdKeyboardArrowUp color="gray.300" />
|
<MdKeyboardArrowUp color="gray.300" />
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
|
import { RefObject, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { Button, Flex, Input, Spinner } from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
MdClose,
|
MdClose,
|
||||||
MdEdit,
|
MdEdit,
|
||||||
@ -9,7 +10,6 @@ import {
|
|||||||
|
|
||||||
import { SelectList, SelectListModel } from '@/components/select/SelectList';
|
import { SelectList, SelectListModel } from '@/components/select/SelectList';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { Button, Flex, Input } from '@/ui';
|
|
||||||
|
|
||||||
export type SelectSingleProps = {
|
export type SelectSingleProps = {
|
||||||
options?: object[];
|
options?: object[];
|
||||||
@ -70,7 +70,7 @@ export const SelectSingle = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!transformedOption) {
|
if (!transformedOption) {
|
||||||
return <></>; // TODO: <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
function onChangeInput(value: string): void {
|
function onChangeInput(value: string): void {
|
||||||
setHasSuggestion(false);
|
setHasSuggestion(false);
|
||||||
@ -105,49 +105,44 @@ export const SelectSingle = ({
|
|||||||
<Flex>
|
<Flex>
|
||||||
<Input
|
<Input
|
||||||
ref={refFocus}
|
ref={refFocus}
|
||||||
style={{
|
width="full"
|
||||||
width: "full",
|
|
||||||
backgroundColor: showList || !selectedOptions ? undefined : 'green.500',
|
|
||||||
borderRadius: "5px 0 0 5px",
|
|
||||||
}}
|
|
||||||
onChange={(e) => onChangeInput(e.target.value)}
|
onChange={(e) => onChangeInput(e.target.value)}
|
||||||
//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={
|
||||||
|
showList || !selectedOptions ? undefined : 'green.800'
|
||||||
|
}
|
||||||
|
borderRadius="5px 0 0 5px"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={onRemoveItem}
|
onClick={onRemoveItem}
|
||||||
// TODO: variant="outline"
|
variant="outline"
|
||||||
style={{
|
borderRadius="0 5px 5px 0"
|
||||||
borderRadius: "0 5px 5px 0",
|
borderWidth="1px 1px 1px 0"
|
||||||
borderWidth: "1px 1px 1px 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{
|
{selectedOptions ? (
|
||||||
selectedOptions ? (
|
<MdClose color="gray.300" />
|
||||||
<MdClose color="gray.300" />
|
) : showList ? (
|
||||||
) : showList ? (
|
<MdKeyboardArrowUp color="gray.300" />
|
||||||
<MdKeyboardArrowUp color="gray.300" />
|
) : hasSuggestion ? (
|
||||||
) : hasSuggestion ? (
|
<MdEdit color="gray.300" />
|
||||||
<MdEdit color="gray.300" />
|
) : (
|
||||||
) : (
|
<MdKeyboardArrowDown color="gray.300" />
|
||||||
<MdKeyboardArrowDown color="gray.300" />
|
)}
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
{
|
{showList && (
|
||||||
showList && (
|
<SelectList
|
||||||
<SelectList
|
options={transformedOption}
|
||||||
options={transformedOption}
|
selected={selectedOptions ? [selectedOptions] : []}
|
||||||
selected={selectedOptions ? [selectedOptions] : []}
|
search={currentSearch}
|
||||||
search={currentSearch}
|
onSelectValue={selectValue}
|
||||||
onSelectValue={selectValue}
|
onCreate={createNewItem}
|
||||||
onCreate={createNewItem}
|
/>
|
||||||
/>
|
)}
|
||||||
)
|
</Flex>
|
||||||
}
|
|
||||||
</Flex >
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
|
import { LuMusic2, LuPlay } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Track } from '@/back-api';
|
import { Track } from '@/back-api';
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
import { ContextMenu, MenuElement } from '@/components/contextMenu/ContextMenu';
|
import { ContextMenu, MenuElement } from '@/components/contextMenu/ContextMenu';
|
||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { Flex, Span } from '@/ui';
|
|
||||||
|
|
||||||
export type DisplayTrackProps = {
|
export type DisplayTrackProps = {
|
||||||
track: Track;
|
track: Track;
|
||||||
@ -23,9 +23,9 @@ export const DisplayTrack = ({
|
|||||||
data={track?.covers}
|
data={track?.covers}
|
||||||
size="50"
|
size="50"
|
||||||
height="full"
|
height="full"
|
||||||
/* TODO: iconEmpty={
|
iconEmpty={
|
||||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
|
||||||
} */
|
}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
@ -33,26 +33,23 @@ export const DisplayTrack = ({
|
|||||||
width="full"
|
width="full"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
style={{
|
overflowX="hidden"
|
||||||
overflowX: "hidden",
|
|
||||||
}}
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<Span
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
|
userSelect="none"
|
||||||
|
marginRight="auto"
|
||||||
|
overflow="hidden"
|
||||||
|
// TODO: noOfLines={[1, 2]}
|
||||||
|
marginY="auto"
|
||||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||||
style={{
|
|
||||||
alignContent: "left",
|
|
||||||
userSelect: "none",
|
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
// TODO: noOfLines={[1, 2]}
|
|
||||||
margin: "auto 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
[{track.track}] {track.name}
|
[{track.track}] {track.name}
|
||||||
</Span>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<ContextMenu elements={contextMenu} />
|
<ContextMenu elements={contextMenu} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
|
import { Flex, Text } from '@chakra-ui/react';
|
||||||
|
import { LuMusic2, LuPlay } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Track } from '@/back-api';
|
import { Track } from '@/back-api';
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
@ -7,7 +10,6 @@ import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
|||||||
import { useSpecificAlbum } from '@/service/Album';
|
import { useSpecificAlbum } from '@/service/Album';
|
||||||
import { useSpecificArtists } from '@/service/Artist';
|
import { useSpecificArtists } from '@/service/Artist';
|
||||||
import { useSpecificGender } from '@/service/Gender';
|
import { useSpecificGender } from '@/service/Gender';
|
||||||
import { Flex, Span } from '@/ui';
|
|
||||||
|
|
||||||
export type DisplayTrackProps = {
|
export type DisplayTrackProps = {
|
||||||
track: Track;
|
track: Track;
|
||||||
@ -28,11 +30,11 @@ export const DisplayTrackFull = ({
|
|||||||
data-testid="display-track-full">
|
data-testid="display-track-full">
|
||||||
<Covers
|
<Covers
|
||||||
data={track?.covers}
|
data={track?.covers}
|
||||||
size="50"
|
size="60px"
|
||||||
marginY="auto"
|
marginY="auto"
|
||||||
/* TODO: iconEmpty={
|
iconEmpty={
|
||||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
|
||||||
} */
|
}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
@ -40,78 +42,69 @@ export const DisplayTrackFull = ({
|
|||||||
width="full"
|
width="full"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
|
overflowX="hidden"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
style={{
|
|
||||||
overflowX: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Span
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
|
userSelect="none"
|
||||||
|
marginRight="auto"
|
||||||
|
overflow="hidden"
|
||||||
|
// TODO: noOfLines={1}
|
||||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||||
style={{
|
|
||||||
alignContent: "left",
|
|
||||||
userSelect: "none",
|
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
// TODO: noOfLines={1}
|
|
||||||
margin: "auto 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{track.name} {track.track && ` [${track.track}]`}
|
{track.name} {track.track && ` [${track.track}]`}
|
||||||
</Span>
|
</Text>
|
||||||
{dataAlbum && (
|
{dataAlbum && (
|
||||||
<Span
|
<Text
|
||||||
fontSize="15px"
|
as="span"
|
||||||
fontWeight="bold"
|
alignContent="left"
|
||||||
//noOfLines={1}
|
|
||||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
|
||||||
style={{
|
|
||||||
alignContent: "left",
|
|
||||||
userSelect: "none",
|
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
// TODO: noOfLines={[1, 2]}
|
|
||||||
margin: "auto 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Span fontWeight="normal">Album:</Span> {dataAlbum.name}
|
|
||||||
</Span>
|
|
||||||
)}
|
|
||||||
{dataArtists && (
|
|
||||||
<Span
|
|
||||||
fontSize="15px"
|
fontSize="15px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
userSelect="none"
|
userSelect="none"
|
||||||
|
marginRight="auto"
|
||||||
|
overflow="hidden"
|
||||||
|
//noOfLines={1}
|
||||||
|
marginY="auto"
|
||||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||||
style={{
|
|
||||||
alignContent: "left",
|
|
||||||
userSelect: "none",
|
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
// TODO: noOfLines={[1, 2]}
|
|
||||||
margin: "auto 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Span fontWeight="normal">Artist(s):</Span> {dataArtists.map((data) => data.name).join(', ')}
|
<Text as="span" fontWeight="normal">Album:</Text> {dataAlbum.name}
|
||||||
</Span>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{dataGender && (
|
{dataArtists && (
|
||||||
<Span
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
fontSize="15px"
|
fontSize="15px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
|
userSelect="none"
|
||||||
|
marginRight="auto"
|
||||||
|
overflow="hidden"
|
||||||
|
//noOfLines={1}
|
||||||
|
marginY="auto"
|
||||||
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||||
style={{
|
|
||||||
alignContent: "left",
|
|
||||||
userSelect: "none",
|
|
||||||
marginRight: "auto",
|
|
||||||
overflow: "hidden",
|
|
||||||
// TODO: noOfLines={[1, 2]}
|
|
||||||
margin: "auto 0",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Span fontWeight="normal">Gender:</Span> {dataGender.name}
|
<Text as="span" fontWeight="normal">Artist(s):</Text> {dataArtists.map((data) => data.name).join(', ')}
|
||||||
</Span>
|
</Text>
|
||||||
|
)}
|
||||||
|
{dataGender && (
|
||||||
|
<Text
|
||||||
|
as="span"
|
||||||
|
alignContent="left"
|
||||||
|
fontSize="15px"
|
||||||
|
fontWeight="bold"
|
||||||
|
userSelect="none"
|
||||||
|
marginRight="auto"
|
||||||
|
overflow="hidden"
|
||||||
|
//noOfLines={1}
|
||||||
|
marginY="auto"
|
||||||
|
color={trackActive?.id === track.id ? 'green.700' : undefined}
|
||||||
|
>
|
||||||
|
<Text as="span" fontWeight="normal">Gender:</Text> {dataGender.name}
|
||||||
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<ContextMenu elements={contextMenu}
|
<ContextMenu elements={contextMenu}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Track } from '@/back-api';
|
import { Track } from '@/back-api';
|
||||||
|
import { Covers } from '@/components/Cover';
|
||||||
import { MenuElement } from '@/components/contextMenu/ContextMenu';
|
import { MenuElement } from '@/components/contextMenu/ContextMenu';
|
||||||
import { useSpecificTrack } from '@/service/Track';
|
import { useSpecificTrack } from '@/service/Track';
|
||||||
import { DisplayTrackFull } from './DisplayTrackFull';
|
import { DisplayTrackFull } from './DisplayTrackFull';
|
||||||
|
@ -1,29 +1,26 @@
|
|||||||
import { Flex } from "@/ui";
|
import { Flex, Skeleton } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
|
||||||
export const DisplayTrackSkeleton = () => {
|
export const DisplayTrackSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<Flex direction="row" width="full" height="full">
|
<Flex direction="row" width="full" height="full">
|
||||||
{/* <Skeleton
|
<Skeleton
|
||||||
borderRadius="0px"
|
borderRadius="0px"
|
||||||
height="50"
|
height="50"
|
||||||
width="50"
|
width="50"
|
||||||
minWidth="50"
|
minWidth="50"
|
||||||
minHeight="50"
|
minHeight="50"
|
||||||
/> */}
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
width="full"
|
width="full"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
style={{
|
overflowX="hidden"
|
||||||
overflowX: "hidden"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* <SkeletonText
|
{/* <SkeletonText
|
||||||
skeletonHeight="20px"
|
skeletonHeight="20px"
|
||||||
noOfLines={1}
|
noOfLines={1}
|
||||||
spacing={0}
|
gap={0}
|
||||||
width="50%"
|
width="50%"
|
||||||
marginY="auto"
|
marginY="auto"
|
||||||
/> */}
|
/> */}
|
||||||
|
74
front/src/components/ui/avatar.tsx
Normal file
74
front/src/components/ui/avatar.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import type { GroupProps, SlotRecipeProps } from "@chakra-ui/react"
|
||||||
|
import { Avatar as ChakraAvatar, Group } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>
|
||||||
|
|
||||||
|
export interface AvatarProps extends ChakraAvatar.RootProps {
|
||||||
|
name?: string
|
||||||
|
src?: string
|
||||||
|
srcSet?: string
|
||||||
|
loading?: ImageProps["loading"]
|
||||||
|
icon?: React.ReactElement
|
||||||
|
fallback?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
||||||
|
function Avatar(props, ref) {
|
||||||
|
const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
|
||||||
|
props
|
||||||
|
return (
|
||||||
|
<ChakraAvatar.Root ref={ref} {...rest}>
|
||||||
|
<AvatarFallback name={name} icon={icon}>
|
||||||
|
{fallback}
|
||||||
|
</AvatarFallback>
|
||||||
|
<ChakraAvatar.Image src={src} srcSet={srcSet} loading={loading} />
|
||||||
|
{children}
|
||||||
|
</ChakraAvatar.Root>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
|
||||||
|
name?: string
|
||||||
|
icon?: React.ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
|
||||||
|
function AvatarFallback(props, ref) {
|
||||||
|
const { name, icon, children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraAvatar.Fallback ref={ref} {...rest}>
|
||||||
|
{children}
|
||||||
|
{name != null && children == null && <>{getInitials(name)}</>}
|
||||||
|
{name == null && children == null && (
|
||||||
|
<ChakraAvatar.Icon asChild={!!icon}>{icon}</ChakraAvatar.Icon>
|
||||||
|
)}
|
||||||
|
</ChakraAvatar.Fallback>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function getInitials(name: string) {
|
||||||
|
const names = name.trim().split(" ")
|
||||||
|
const firstName = names[0] != null ? names[0] : ""
|
||||||
|
const lastName = names.length > 1 ? names[names.length - 1] : ""
|
||||||
|
return firstName && lastName
|
||||||
|
? `${firstName.charAt(0)}${lastName.charAt(0)}`
|
||||||
|
: firstName.charAt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvatarGroupProps extends GroupProps, SlotRecipeProps<"avatar"> {}
|
||||||
|
|
||||||
|
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
|
||||||
|
function AvatarGroup(props, ref) {
|
||||||
|
const { size, variant, borderless, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
|
||||||
|
<Group gap="0" spaceX="-3" ref={ref} {...rest} />
|
||||||
|
</ChakraAvatar.PropsProvider>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
40
front/src/components/ui/button.tsx
Normal file
40
front/src/components/ui/button.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import type { ButtonProps as ChakraButtonProps } from "@chakra-ui/react"
|
||||||
|
import {
|
||||||
|
AbsoluteCenter,
|
||||||
|
Button as ChakraButton,
|
||||||
|
Span,
|
||||||
|
Spinner,
|
||||||
|
} from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
interface ButtonLoadingProps {
|
||||||
|
loading?: boolean
|
||||||
|
loadingText?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
|
||||||
|
|
||||||
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
function Button(props, ref) {
|
||||||
|
const { loading, disabled, loadingText, children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraButton disabled={loading || disabled} ref={ref} {...rest}>
|
||||||
|
{loading && !loadingText ? (
|
||||||
|
<>
|
||||||
|
<AbsoluteCenter display="inline-flex">
|
||||||
|
<Spinner size="inherit" color="inherit" />
|
||||||
|
</AbsoluteCenter>
|
||||||
|
<Span opacity={0}>{children}</Span>
|
||||||
|
</>
|
||||||
|
) : loading && loadingText ? (
|
||||||
|
<>
|
||||||
|
<Spinner size="inherit" color="inherit" />
|
||||||
|
{loadingText}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</ChakraButton>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
25
front/src/components/ui/checkbox.tsx
Normal file
25
front/src/components/ui/checkbox.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Checkbox as ChakraCheckbox } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface CheckboxProps extends ChakraCheckbox.RootProps {
|
||||||
|
icon?: React.ReactNode
|
||||||
|
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||||
|
rootRef?: React.Ref<HTMLLabelElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
||||||
|
function Checkbox(props, ref) {
|
||||||
|
const { icon, children, inputProps, rootRef, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraCheckbox.Root ref={rootRef} {...rest}>
|
||||||
|
<ChakraCheckbox.HiddenInput ref={ref} {...inputProps} />
|
||||||
|
<ChakraCheckbox.Control>
|
||||||
|
{icon || <ChakraCheckbox.Indicator />}
|
||||||
|
</ChakraCheckbox.Control>
|
||||||
|
{children != null && (
|
||||||
|
<ChakraCheckbox.Label>{children}</ChakraCheckbox.Label>
|
||||||
|
)}
|
||||||
|
</ChakraCheckbox.Root>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
17
front/src/components/ui/close-button.tsx
Normal file
17
front/src/components/ui/close-button.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { ButtonProps } from "@chakra-ui/react"
|
||||||
|
import { IconButton as ChakraIconButton } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
import { LuX } from "react-icons/lu"
|
||||||
|
|
||||||
|
export type CloseButtonProps = ButtonProps
|
||||||
|
|
||||||
|
export const CloseButton = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
CloseButtonProps
|
||||||
|
>(function CloseButton(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraIconButton variant="ghost" aria-label="Close" ref={ref} {...props}>
|
||||||
|
{props.children ?? <LuX />}
|
||||||
|
</ChakraIconButton>
|
||||||
|
)
|
||||||
|
})
|
75
front/src/components/ui/color-mode.tsx
Normal file
75
front/src/components/ui/color-mode.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import type { IconButtonProps } from "@chakra-ui/react"
|
||||||
|
import { ClientOnly, IconButton, Skeleton } from "@chakra-ui/react"
|
||||||
|
import { ThemeProvider, useTheme } from "next-themes"
|
||||||
|
import type { ThemeProviderProps } from "next-themes"
|
||||||
|
import * as React from "react"
|
||||||
|
import { LuMoon, LuSun } from "react-icons/lu"
|
||||||
|
|
||||||
|
export interface ColorModeProviderProps extends ThemeProviderProps {}
|
||||||
|
|
||||||
|
export function ColorModeProvider(props: ColorModeProviderProps) {
|
||||||
|
return (
|
||||||
|
<ThemeProvider attribute="class" disableTransitionOnChange {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ColorMode = "light" | "dark"
|
||||||
|
|
||||||
|
export interface UseColorModeReturn {
|
||||||
|
colorMode: ColorMode
|
||||||
|
setColorMode: (colorMode: ColorMode) => void
|
||||||
|
toggleColorMode: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useColorMode(): UseColorModeReturn {
|
||||||
|
const { resolvedTheme, setTheme } = useTheme()
|
||||||
|
const toggleColorMode = () => {
|
||||||
|
setTheme(resolvedTheme === "light" ? "dark" : "light")
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
colorMode: resolvedTheme as ColorMode,
|
||||||
|
setColorMode: setTheme,
|
||||||
|
toggleColorMode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useColorModeValue<T>(light: T, dark: T) {
|
||||||
|
const { colorMode } = useColorMode()
|
||||||
|
return colorMode === "dark" ? dark : light
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ColorModeIcon() {
|
||||||
|
const { colorMode } = useColorMode()
|
||||||
|
return colorMode === "dark" ? <LuMoon /> : <LuSun />
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ColorModeButtonProps extends Omit<IconButtonProps, "aria-label"> {}
|
||||||
|
|
||||||
|
export const ColorModeButton = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
ColorModeButtonProps
|
||||||
|
>(function ColorModeButton(props, ref) {
|
||||||
|
const { toggleColorMode } = useColorMode()
|
||||||
|
return (
|
||||||
|
<ClientOnly fallback={<Skeleton boxSize="8" />}>
|
||||||
|
<IconButton
|
||||||
|
onClick={toggleColorMode}
|
||||||
|
variant="ghost"
|
||||||
|
aria-label="Toggle color mode"
|
||||||
|
size="sm"
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
css={{
|
||||||
|
_icon: {
|
||||||
|
width: "5",
|
||||||
|
height: "5",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ColorModeIcon />
|
||||||
|
</IconButton>
|
||||||
|
</ClientOnly>
|
||||||
|
)
|
||||||
|
})
|
62
front/src/components/ui/dialog.tsx
Normal file
62
front/src/components/ui/dialog.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Dialog as ChakraDialog, Portal } from "@chakra-ui/react"
|
||||||
|
import { CloseButton } from "./close-button"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
interface DialogContentProps extends ChakraDialog.ContentProps {
|
||||||
|
portalled?: boolean
|
||||||
|
portalRef?: React.RefObject<HTMLElement>
|
||||||
|
backdrop?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DialogContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
DialogContentProps
|
||||||
|
>(function DialogContent(props, ref) {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
portalled = true,
|
||||||
|
portalRef,
|
||||||
|
backdrop = true,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal disabled={!portalled} container={portalRef}>
|
||||||
|
{backdrop && <ChakraDialog.Backdrop />}
|
||||||
|
<ChakraDialog.Positioner>
|
||||||
|
<ChakraDialog.Content ref={ref} {...rest} asChild={false}>
|
||||||
|
{children}
|
||||||
|
</ChakraDialog.Content>
|
||||||
|
</ChakraDialog.Positioner>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const DialogCloseTrigger = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
ChakraDialog.CloseTriggerProps
|
||||||
|
>(function DialogCloseTrigger(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraDialog.CloseTrigger
|
||||||
|
position="absolute"
|
||||||
|
top="2"
|
||||||
|
insetEnd="2"
|
||||||
|
{...props}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<CloseButton size="sm" ref={ref}>
|
||||||
|
{props.children}
|
||||||
|
</CloseButton>
|
||||||
|
</ChakraDialog.CloseTrigger>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const DialogRoot = ChakraDialog.Root
|
||||||
|
export const DialogFooter = ChakraDialog.Footer
|
||||||
|
export const DialogHeader = ChakraDialog.Header
|
||||||
|
export const DialogBody = ChakraDialog.Body
|
||||||
|
export const DialogBackdrop = ChakraDialog.Backdrop
|
||||||
|
export const DialogTitle = ChakraDialog.Title
|
||||||
|
export const DialogDescription = ChakraDialog.Description
|
||||||
|
export const DialogTrigger = ChakraDialog.Trigger
|
||||||
|
export const DialogActionTrigger = ChakraDialog.ActionTrigger
|
52
front/src/components/ui/drawer.tsx
Normal file
52
front/src/components/ui/drawer.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Drawer as ChakraDrawer, Portal } from "@chakra-ui/react"
|
||||||
|
import { CloseButton } from "./close-button"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
interface DrawerContentProps extends ChakraDrawer.ContentProps {
|
||||||
|
portalled?: boolean
|
||||||
|
portalRef?: React.RefObject<HTMLElement>
|
||||||
|
offset?: ChakraDrawer.ContentProps["padding"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DrawerContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
DrawerContentProps
|
||||||
|
>(function DrawerContent(props, ref) {
|
||||||
|
const { children, portalled = true, portalRef, offset, ...rest } = props
|
||||||
|
return (
|
||||||
|
<Portal disabled={!portalled} container={portalRef}>
|
||||||
|
<ChakraDrawer.Positioner padding={offset}>
|
||||||
|
<ChakraDrawer.Content ref={ref} {...rest} asChild={false}>
|
||||||
|
{children}
|
||||||
|
</ChakraDrawer.Content>
|
||||||
|
</ChakraDrawer.Positioner>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const DrawerCloseTrigger = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
ChakraDrawer.CloseTriggerProps
|
||||||
|
>(function DrawerCloseTrigger(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraDrawer.CloseTrigger
|
||||||
|
position="absolute"
|
||||||
|
top="2"
|
||||||
|
insetEnd="2"
|
||||||
|
{...props}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<CloseButton size="sm" ref={ref} />
|
||||||
|
</ChakraDrawer.CloseTrigger>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const DrawerTrigger = ChakraDrawer.Trigger
|
||||||
|
export const DrawerRoot = ChakraDrawer.Root
|
||||||
|
export const DrawerFooter = ChakraDrawer.Footer
|
||||||
|
export const DrawerHeader = ChakraDrawer.Header
|
||||||
|
export const DrawerBody = ChakraDrawer.Body
|
||||||
|
export const DrawerBackdrop = ChakraDrawer.Backdrop
|
||||||
|
export const DrawerDescription = ChakraDrawer.Description
|
||||||
|
export const DrawerTitle = ChakraDrawer.Title
|
||||||
|
export const DrawerActionTrigger = ChakraDrawer.ActionTrigger
|
33
front/src/components/ui/field.tsx
Normal file
33
front/src/components/ui/field.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Field as ChakraField } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface FieldProps extends Omit<ChakraField.RootProps, "label"> {
|
||||||
|
label?: React.ReactNode
|
||||||
|
helperText?: React.ReactNode
|
||||||
|
errorText?: React.ReactNode
|
||||||
|
optionalText?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
||||||
|
function Field(props, ref) {
|
||||||
|
const { label, children, helperText, errorText, optionalText, ...rest } =
|
||||||
|
props
|
||||||
|
return (
|
||||||
|
<ChakraField.Root ref={ref} {...rest}>
|
||||||
|
{label && (
|
||||||
|
<ChakraField.Label>
|
||||||
|
{label}
|
||||||
|
<ChakraField.RequiredIndicator fallback={optionalText} />
|
||||||
|
</ChakraField.Label>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
{helperText && (
|
||||||
|
<ChakraField.HelperText>{helperText}</ChakraField.HelperText>
|
||||||
|
)}
|
||||||
|
{errorText && (
|
||||||
|
<ChakraField.ErrorText>{errorText}</ChakraField.ErrorText>
|
||||||
|
)}
|
||||||
|
</ChakraField.Root>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
53
front/src/components/ui/input-group.tsx
Normal file
53
front/src/components/ui/input-group.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import type { BoxProps, InputElementProps } from "@chakra-ui/react"
|
||||||
|
import { Group, InputElement } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface InputGroupProps extends BoxProps {
|
||||||
|
startElementProps?: InputElementProps
|
||||||
|
endElementProps?: InputElementProps
|
||||||
|
startElement?: React.ReactNode
|
||||||
|
endElement?: React.ReactNode
|
||||||
|
children: React.ReactElement<InputElementProps>
|
||||||
|
startOffset?: InputElementProps["paddingStart"]
|
||||||
|
endOffset?: InputElementProps["paddingEnd"]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
||||||
|
function InputGroup(props, ref) {
|
||||||
|
const {
|
||||||
|
startElement,
|
||||||
|
startElementProps,
|
||||||
|
endElement,
|
||||||
|
endElementProps,
|
||||||
|
children,
|
||||||
|
startOffset = "6px",
|
||||||
|
endOffset = "6px",
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const child =
|
||||||
|
React.Children.only<React.ReactElement<InputElementProps>>(children)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group ref={ref} {...rest}>
|
||||||
|
{startElement && (
|
||||||
|
<InputElement pointerEvents="none" {...startElementProps}>
|
||||||
|
{startElement}
|
||||||
|
</InputElement>
|
||||||
|
)}
|
||||||
|
{React.cloneElement(child, {
|
||||||
|
...(startElement && {
|
||||||
|
ps: `calc(var(--input-height) - ${startOffset})`,
|
||||||
|
}),
|
||||||
|
...(endElement && { pe: `calc(var(--input-height) - ${endOffset})` }),
|
||||||
|
...children.props,
|
||||||
|
})}
|
||||||
|
{endElement && (
|
||||||
|
<InputElement placement="end" {...endElementProps}>
|
||||||
|
{endElement}
|
||||||
|
</InputElement>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
110
front/src/components/ui/menu.tsx
Normal file
110
front/src/components/ui/menu.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { AbsoluteCenter, Menu as ChakraMenu, Portal } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
import { LuCheck, LuChevronRight } from "react-icons/lu"
|
||||||
|
|
||||||
|
interface MenuContentProps extends ChakraMenu.ContentProps {
|
||||||
|
portalled?: boolean
|
||||||
|
portalRef?: React.RefObject<HTMLElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(
|
||||||
|
function MenuContent(props, ref) {
|
||||||
|
const { portalled = true, portalRef, ...rest } = props
|
||||||
|
return (
|
||||||
|
<Portal disabled={!portalled} container={portalRef}>
|
||||||
|
<ChakraMenu.Positioner>
|
||||||
|
<ChakraMenu.Content ref={ref} {...rest} />
|
||||||
|
</ChakraMenu.Positioner>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const MenuArrow = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ChakraMenu.ArrowProps
|
||||||
|
>(function MenuArrow(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraMenu.Arrow ref={ref} {...props}>
|
||||||
|
<ChakraMenu.ArrowTip />
|
||||||
|
</ChakraMenu.Arrow>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const MenuCheckboxItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ChakraMenu.CheckboxItemProps
|
||||||
|
>(function MenuCheckboxItem(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraMenu.CheckboxItem ref={ref} {...props}>
|
||||||
|
<ChakraMenu.ItemIndicator hidden={false}>
|
||||||
|
<LuCheck />
|
||||||
|
</ChakraMenu.ItemIndicator>
|
||||||
|
{props.children}
|
||||||
|
</ChakraMenu.CheckboxItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const MenuRadioItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ChakraMenu.RadioItemProps
|
||||||
|
>(function MenuRadioItem(props, ref) {
|
||||||
|
const { children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraMenu.RadioItem ps="8" ref={ref} {...rest}>
|
||||||
|
<AbsoluteCenter axis="horizontal" left="4" asChild>
|
||||||
|
<ChakraMenu.ItemIndicator>
|
||||||
|
<LuCheck />
|
||||||
|
</ChakraMenu.ItemIndicator>
|
||||||
|
</AbsoluteCenter>
|
||||||
|
<ChakraMenu.ItemText>{children}</ChakraMenu.ItemText>
|
||||||
|
</ChakraMenu.RadioItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const MenuItemGroup = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ChakraMenu.ItemGroupProps
|
||||||
|
>(function MenuItemGroup(props, ref) {
|
||||||
|
const { title, children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraMenu.ItemGroup ref={ref} {...rest}>
|
||||||
|
{title && (
|
||||||
|
<ChakraMenu.ItemGroupLabel userSelect="none">
|
||||||
|
{title}
|
||||||
|
</ChakraMenu.ItemGroupLabel>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</ChakraMenu.ItemGroup>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
|
||||||
|
startIcon?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MenuTriggerItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
MenuTriggerItemProps
|
||||||
|
>(function MenuTriggerItem(props, ref) {
|
||||||
|
const { startIcon, children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraMenu.TriggerItem ref={ref} {...rest}>
|
||||||
|
{startIcon}
|
||||||
|
{children}
|
||||||
|
<LuChevronRight />
|
||||||
|
</ChakraMenu.TriggerItem>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup
|
||||||
|
export const MenuContextTrigger = ChakraMenu.ContextTrigger
|
||||||
|
export const MenuRoot = ChakraMenu.Root
|
||||||
|
export const MenuSeparator = ChakraMenu.Separator
|
||||||
|
|
||||||
|
export const MenuItem = ChakraMenu.Item
|
||||||
|
export const MenuItemText = ChakraMenu.ItemText
|
||||||
|
export const MenuItemCommand = ChakraMenu.ItemCommand
|
||||||
|
export const MenuTrigger = ChakraMenu.Trigger
|
24
front/src/components/ui/number-input.tsx
Normal file
24
front/src/components/ui/number-input.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { NumberInput as ChakraNumberInput } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface NumberInputProps extends ChakraNumberInput.RootProps {}
|
||||||
|
|
||||||
|
export const NumberInputRoot = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
NumberInputProps
|
||||||
|
>(function NumberInput(props, ref) {
|
||||||
|
const { children, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraNumberInput.Root ref={ref} variant="outline" {...rest}>
|
||||||
|
{children}
|
||||||
|
<ChakraNumberInput.Control>
|
||||||
|
<ChakraNumberInput.IncrementTrigger />
|
||||||
|
<ChakraNumberInput.DecrementTrigger />
|
||||||
|
</ChakraNumberInput.Control>
|
||||||
|
</ChakraNumberInput.Root>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const NumberInputField = ChakraNumberInput.Input
|
||||||
|
export const NumberInputScrubber = ChakraNumberInput.Scrubber
|
||||||
|
export const NumberInputLabel = ChakraNumberInput.Label
|
59
front/src/components/ui/popover.tsx
Normal file
59
front/src/components/ui/popover.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Popover as ChakraPopover, Portal } from "@chakra-ui/react"
|
||||||
|
import { CloseButton } from "./close-button"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
interface PopoverContentProps extends ChakraPopover.ContentProps {
|
||||||
|
portalled?: boolean
|
||||||
|
portalRef?: React.RefObject<HTMLElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopoverContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
PopoverContentProps
|
||||||
|
>(function PopoverContent(props, ref) {
|
||||||
|
const { portalled = true, portalRef, ...rest } = props
|
||||||
|
return (
|
||||||
|
<Portal disabled={!portalled} container={portalRef}>
|
||||||
|
<ChakraPopover.Positioner>
|
||||||
|
<ChakraPopover.Content ref={ref} {...rest} />
|
||||||
|
</ChakraPopover.Positioner>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const PopoverArrow = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
ChakraPopover.ArrowProps
|
||||||
|
>(function PopoverArrow(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraPopover.Arrow {...props} ref={ref}>
|
||||||
|
<ChakraPopover.ArrowTip />
|
||||||
|
</ChakraPopover.Arrow>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const PopoverCloseTrigger = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
ChakraPopover.CloseTriggerProps
|
||||||
|
>(function PopoverCloseTrigger(props, ref) {
|
||||||
|
return (
|
||||||
|
<ChakraPopover.CloseTrigger
|
||||||
|
position="absolute"
|
||||||
|
top="1"
|
||||||
|
insetEnd="1"
|
||||||
|
{...props}
|
||||||
|
asChild
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<CloseButton size="sm" />
|
||||||
|
</ChakraPopover.CloseTrigger>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export const PopoverTitle = ChakraPopover.Title
|
||||||
|
export const PopoverDescription = ChakraPopover.Description
|
||||||
|
export const PopoverFooter = ChakraPopover.Footer
|
||||||
|
export const PopoverHeader = ChakraPopover.Header
|
||||||
|
export const PopoverRoot = ChakraPopover.Root
|
||||||
|
export const PopoverBody = ChakraPopover.Body
|
||||||
|
export const PopoverTrigger = ChakraPopover.Trigger
|
15
front/src/components/ui/provider.tsx
Normal file
15
front/src/components/ui/provider.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
|
||||||
|
import {
|
||||||
|
ColorModeProvider,
|
||||||
|
type ColorModeProviderProps,
|
||||||
|
} from "./color-mode"
|
||||||
|
|
||||||
|
export function Provider(props: ColorModeProviderProps) {
|
||||||
|
return (
|
||||||
|
<ChakraProvider value={defaultSystem}>
|
||||||
|
<ColorModeProvider {...props} />
|
||||||
|
</ChakraProvider>
|
||||||
|
)
|
||||||
|
}
|
24
front/src/components/ui/radio.tsx
Normal file
24
front/src/components/ui/radio.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface RadioProps extends ChakraRadioGroup.ItemProps {
|
||||||
|
rootRef?: React.Ref<HTMLDivElement>
|
||||||
|
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
|
||||||
|
function Radio(props, ref) {
|
||||||
|
const { children, inputProps, rootRef, ...rest } = props
|
||||||
|
return (
|
||||||
|
<ChakraRadioGroup.Item ref={rootRef} {...rest}>
|
||||||
|
<ChakraRadioGroup.ItemHiddenInput ref={ref} {...inputProps} />
|
||||||
|
<ChakraRadioGroup.ItemIndicator />
|
||||||
|
{children && (
|
||||||
|
<ChakraRadioGroup.ItemText>{children}</ChakraRadioGroup.ItemText>
|
||||||
|
)}
|
||||||
|
</ChakraRadioGroup.Item>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const RadioGroup = ChakraRadioGroup.Root
|
82
front/src/components/ui/slider.tsx
Normal file
82
front/src/components/ui/slider.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { Slider as ChakraSlider, For, HStack } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface SliderProps extends ChakraSlider.RootProps {
|
||||||
|
marks?: Array<number | { value: number; label: React.ReactNode }>
|
||||||
|
label?: React.ReactNode
|
||||||
|
showValue?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
|
||||||
|
function Slider(props, ref) {
|
||||||
|
const { marks: marksProp, label, showValue, ...rest } = props
|
||||||
|
const value = props.defaultValue ?? props.value
|
||||||
|
|
||||||
|
const marks = marksProp?.map((mark) => {
|
||||||
|
if (typeof mark === "number") return { value: mark, label: undefined }
|
||||||
|
return mark
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasMarkLabel = !!marks?.some((mark) => mark.label)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChakraSlider.Root ref={ref} thumbAlignment="center" {...rest}>
|
||||||
|
{label && !showValue && (
|
||||||
|
<ChakraSlider.Label>{label}</ChakraSlider.Label>
|
||||||
|
)}
|
||||||
|
{label && showValue && (
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<ChakraSlider.Label>{label}</ChakraSlider.Label>
|
||||||
|
<ChakraSlider.ValueText />
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
<ChakraSlider.Control data-has-mark-label={hasMarkLabel || undefined}>
|
||||||
|
<ChakraSlider.Track>
|
||||||
|
<ChakraSlider.Range />
|
||||||
|
</ChakraSlider.Track>
|
||||||
|
<SliderThumbs value={value} />
|
||||||
|
<SliderMarks marks={marks} />
|
||||||
|
</ChakraSlider.Control>
|
||||||
|
</ChakraSlider.Root>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function SliderThumbs(props: { value?: number[] }) {
|
||||||
|
const { value } = props
|
||||||
|
return (
|
||||||
|
<For each={value}>
|
||||||
|
{(_, index) => (
|
||||||
|
<ChakraSlider.Thumb key={index} index={index}>
|
||||||
|
<ChakraSlider.HiddenInput />
|
||||||
|
</ChakraSlider.Thumb>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SliderMarksProps {
|
||||||
|
marks?: Array<number | { value: number; label: React.ReactNode }>
|
||||||
|
}
|
||||||
|
|
||||||
|
const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>(
|
||||||
|
function SliderMarks(props, ref) {
|
||||||
|
const { marks } = props
|
||||||
|
if (!marks?.length) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChakraSlider.MarkerGroup ref={ref}>
|
||||||
|
{marks.map((mark, index) => {
|
||||||
|
const value = typeof mark === "number" ? mark : mark.value
|
||||||
|
const label = typeof mark === "number" ? undefined : mark.label
|
||||||
|
return (
|
||||||
|
<ChakraSlider.Marker key={index} value={value}>
|
||||||
|
<ChakraSlider.MarkerIndicator />
|
||||||
|
{label}
|
||||||
|
</ChakraSlider.Marker>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ChakraSlider.MarkerGroup>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
@ -1,39 +1,51 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { RestErrorResponse } from "@/back-api";
|
import { RestErrorResponse } from "@/back-api";
|
||||||
import { HStack } from "@/ui";
|
import {
|
||||||
|
Toaster as ChakraToaster,
|
||||||
|
Portal,
|
||||||
|
Spinner,
|
||||||
|
Stack,
|
||||||
|
Toast,
|
||||||
|
createToaster,
|
||||||
|
} from "@chakra-ui/react"
|
||||||
|
|
||||||
// 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({
|
||||||
// title: `[${error.status}] ${error.statusMessage}`,
|
title: `[${error.status}] ${error.statusMessage}`,
|
||||||
// description: error.message,
|
description: error.message,
|
||||||
// });
|
});
|
||||||
// };
|
};
|
||||||
|
|
||||||
// export const Toaster = () => {
|
export const Toaster = () => {
|
||||||
// return (
|
return (
|
||||||
// <Portal>
|
<Portal>
|
||||||
// <ArkToaster toaster={toaster}>
|
<ChakraToaster toaster={toaster} insetInline={{ mdDown: "4" }}>
|
||||||
// {(toast) => (
|
{(toast) => (
|
||||||
// <Toast.Root>
|
<Toast.Root width={{ md: "sm" }}>
|
||||||
// <HStack spacing="1" style={{ width: "100%" }}>
|
{toast.type === "loading" ? (
|
||||||
// {toast.title && <Toast.Title>{toast.title}</Toast.Title>}
|
<Spinner size="sm" color="blue.solid" />
|
||||||
// {toast.description && (
|
) : (
|
||||||
// <Toast.Description>{toast.description}</Toast.Description>
|
<Toast.Indicator />
|
||||||
// )}
|
)}
|
||||||
// </HStack>
|
<Stack gap="1" flex="1" maxWidth="100%">
|
||||||
// {toast.action && (
|
{toast.title && <Toast.Title>{toast.title}</Toast.Title>}
|
||||||
// <Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
|
{toast.description && (
|
||||||
// )}
|
<Toast.Description>{toast.description}</Toast.Description>
|
||||||
// {toast.meta?.closable && <Toast.CloseTrigger />}
|
)}
|
||||||
// </Toast.Root>
|
</Stack>
|
||||||
// )}
|
{toast.action && (
|
||||||
// </ArkToaster>
|
<Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>
|
||||||
// </Portal>
|
)}
|
||||||
// )
|
{toast.meta?.closable && <Toast.CloseTrigger />}
|
||||||
// }
|
</Toast.Root>
|
||||||
|
)}
|
||||||
|
</ChakraToaster>
|
||||||
|
</Portal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
46
front/src/components/ui/tooltip.tsx
Normal file
46
front/src/components/ui/tooltip.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
export interface TooltipProps extends ChakraTooltip.RootProps {
|
||||||
|
showArrow?: boolean
|
||||||
|
portalled?: boolean
|
||||||
|
portalRef?: React.RefObject<HTMLElement>
|
||||||
|
content: React.ReactNode
|
||||||
|
contentProps?: ChakraTooltip.ContentProps
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||||
|
function Tooltip(props, ref) {
|
||||||
|
const {
|
||||||
|
showArrow,
|
||||||
|
children,
|
||||||
|
disabled,
|
||||||
|
portalled = true,
|
||||||
|
content,
|
||||||
|
contentProps,
|
||||||
|
portalRef,
|
||||||
|
...rest
|
||||||
|
} = props
|
||||||
|
|
||||||
|
if (disabled) return children
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChakraTooltip.Root {...rest}>
|
||||||
|
<ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
|
||||||
|
<Portal disabled={!portalled} container={portalRef}>
|
||||||
|
<ChakraTooltip.Positioner>
|
||||||
|
<ChakraTooltip.Content ref={ref} {...contentProps}>
|
||||||
|
{showArrow && (
|
||||||
|
<ChakraTooltip.Arrow>
|
||||||
|
<ChakraTooltip.ArrowTip />
|
||||||
|
</ChakraTooltip.Arrow>
|
||||||
|
)}
|
||||||
|
{content}
|
||||||
|
</ChakraTooltip.Content>
|
||||||
|
</ChakraTooltip.Positioner>
|
||||||
|
</Portal>
|
||||||
|
</ChakraTooltip.Root>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
28
front/src/config/dayjs.ts
Normal file
28
front/src/config/dayjs.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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);
|
2
front/src/config/index.ts
Normal file
2
front/src/config/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import './axios';
|
||||||
|
import './dayjs';
|
@ -1,4 +1,4 @@
|
|||||||
export const BASE_WRAP_SPACING = "5px"; // { base: "5px", md: "10px", lg: "20px" };
|
export const BASE_WRAP_SPACING = { base: "5px", md: "10px", lg: "20px" };
|
||||||
export const BASE_WRAP_WIDTH = "90%"; // { base: "90%", md: "45%", lg: "270px" };
|
export const BASE_WRAP_WIDTH = { base: "90%", md: "45%", lg: "270px" };
|
||||||
export const BASE_WRAP_HEIGHT = "75px"; // { base: "75px", lg: "120px" };
|
export const BASE_WRAP_HEIGHT = { base: "75px", lg: "120px" };
|
||||||
export const BASE_WRAP_ICON_SIZE = "50px";// { base: "50px", lg: "100px" };
|
export const BASE_WRAP_ICON_SIZE = { base: "50px", lg: "100px" };
|
@ -1,27 +1,26 @@
|
|||||||
|
import { Box, Button, 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';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { Flex, Link, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const Error401 = () => {
|
export const Error401 = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<PageLayoutInfoCenter padding="25px" width="75%">
|
<PageLayoutInfoCenter padding="25px">
|
||||||
<Flex align="center">
|
<Center>
|
||||||
<MdControlCamera size="250px" color="red.600" />
|
<MdControlCamera style={{ width: "250px", height: "250px", color: "orange" }} />
|
||||||
</Flex>
|
</Center>
|
||||||
<Flex style={{ textAlign: "center" }}>
|
<Box textAlign="center">
|
||||||
<Text fontSize="px">Erreur 401</Text>
|
<Heading>Erreur 401</Heading>
|
||||||
<Text color="red.600">
|
<Text color="red.600">
|
||||||
Vous n'êtes pas autorisé a accéder a ce contenu.
|
Vous n'êtes pas autorisé a accéder a ce contenu.
|
||||||
</Text>
|
</Text>
|
||||||
<Link href="/">
|
<Link as="a" href="/">
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Box>
|
||||||
</PageLayoutInfoCenter>
|
</PageLayoutInfoCenter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
import { MdControlCamera } from 'react-icons/md';
|
import { Box, Button, Center, Heading, Link, Text } from '@chakra-ui/react';
|
||||||
|
import { MdDangerous } from 'react-icons/md';
|
||||||
|
|
||||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { Flex, Link, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const Error403 = () => {
|
export const Error403 = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<PageLayoutInfoCenter padding="25px" width="75%">
|
<PageLayoutInfoCenter padding="25px">
|
||||||
<Flex align="center">
|
<Center>
|
||||||
<MdControlCamera size="250px" color="red.600" />
|
<MdDangerous style={{ width: "250px", height: "250px", color: "red" }} />
|
||||||
</Flex>
|
</Center>
|
||||||
<Flex style={{ textAlign: "center" }}>
|
<Box textAlign="center">
|
||||||
<Text fontSize="px">Erreur 403</Text>
|
<Heading>Erreur 403</Heading>
|
||||||
<Text color="red.600">
|
<Text color="orange.600">Cette page vous est interdite</Text>
|
||||||
Cette page vous est interdite.
|
|
||||||
</Text>
|
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Box>
|
||||||
</PageLayoutInfoCenter>
|
</PageLayoutInfoCenter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { MdControlCamera } from 'react-icons/md';
|
import { Box, Button, Center, Heading, Link, Text } from '@chakra-ui/react';
|
||||||
|
import { MdSignpost } from 'react-icons/md';
|
||||||
|
|
||||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { Flex, Link, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const Error404 = () => {
|
export const Error404 = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<PageLayoutInfoCenter padding="25px" width="75%">
|
<PageLayoutInfoCenter padding="25px">
|
||||||
<Flex align="center">
|
<Center>
|
||||||
<MdControlCamera size="250px" color="red.600" />
|
<MdSignpost style={{ width: "250px", height: "250px", color: "aqua" }} />
|
||||||
</Flex>
|
</Center>
|
||||||
<Flex style={{ textAlign: "center" }}>
|
<Box textAlign="center">
|
||||||
<Text fontSize="px">Erreur 404</Text>
|
<Heading>Erreur 404</Heading>
|
||||||
<Text color="red.600">
|
<Text color="gray.600">
|
||||||
Cette page n'existe plus ou l'URL a changé.
|
Cette page n'existe plus ou l'URL a changé
|
||||||
</Text>
|
</Text>
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
Retour à l'accueil
|
Retour à l'accueil
|
||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Box>
|
||||||
</PageLayoutInfoCenter>
|
</PageLayoutInfoCenter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,44 +1,30 @@
|
|||||||
import { Button, Flex, Text } from '@/ui';
|
|
||||||
import { useDisclosure } from '@/utils/disclosure';
|
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AlertDescription,
|
||||||
|
AlertTitle,
|
||||||
|
Box,
|
||||||
|
Alert,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FallbackProps,
|
FallbackProps,
|
||||||
ErrorBoundary as ReactErrorBoundary,
|
ErrorBoundary as ReactErrorBoundary,
|
||||||
} from 'react-error-boundary';
|
} from 'react-error-boundary';
|
||||||
import { LuChevronUp, LuChevronDown } from 'react-icons/lu';
|
|
||||||
|
|
||||||
const ErrorFallback = ({ error }: FallbackProps) => {
|
const ErrorFallback = ({ error }: FallbackProps) => {
|
||||||
const { open, onToggle } = useDisclosure();
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" style={{ padding: 4, margin: "auto", backgroundColor: "red.500" }}>
|
<Box padding="8" margin="auto">
|
||||||
<Text color='red'>An unexpected error has occurred.</Text>
|
<Alert.Root status="error" borderRadius="md">
|
||||||
<Text>Message: {error.message}</Text>
|
<Alert.Indicator height="75px" width="75px" />
|
||||||
{/*
|
<Box flex="1">
|
||||||
<AlertRoot status="error" borderRadius="md">
|
<AlertTitle fontWeight="bold" fontSize="35px">An unexpected error has occurred.</AlertTitle>
|
||||||
<Flex style={{ flex: "1" }}>
|
<AlertDescription padding="5" marginTop="3" fontSize="20px" lineHeight="1.4">
|
||||||
<AlertTitle>An unexpected error has occurred.</AlertTitle>
|
<br />
|
||||||
<AlertDescription display="block" lineHeight="1.4">
|
{error.message}
|
||||||
<Button
|
|
||||||
//theme="@secondary"
|
|
||||||
color="red.800"
|
|
||||||
//size="sm"
|
|
||||||
onClick={onToggle}
|
|
||||||
>
|
|
||||||
Show details {open ? <LuChevronUp /> : <LuChevronDown />}
|
|
||||||
</Button>
|
|
||||||
<Collapsible.Root open={open}>
|
|
||||||
<Collapsible.Content>
|
|
||||||
<Flex mt={4} fontFamily="monospace">
|
|
||||||
{error.message}
|
|
||||||
</Flex>
|
|
||||||
</Collapsible.Content>
|
|
||||||
</Collapsible.Root>
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Flex>
|
</Box>
|
||||||
</AlertRoot> */}
|
</Alert.Root>
|
||||||
</Flex >
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
|
import { createIcon } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export const DoubleArrowIcon = createIcon({
|
||||||
export const DoubleArrowIcon = <></>;
|
displayName: 'DoubleArrowIcon',
|
||||||
// createIcon({
|
viewBox: '0 0 24 24',
|
||||||
// displayName: 'DoubleArrowIcon',
|
path: (
|
||||||
// viewBox: '0 0 24 24',
|
<path
|
||||||
// path: (
|
fillRule="evenodd"
|
||||||
// <path
|
clipRule="evenodd"
|
||||||
// fillRule="evenodd"
|
d="M1.293 12.207a1 1 0 0 1 0-1.414l6.364-6.364A1 1 0 0 1 9.07 5.843L4.414 10.5h15.172l-4.657-4.657a1 1 0 0 1 1.414-1.414l6.364 6.364a1 1 0 0 1 0 1.414l-6.364 6.364a1 1 0 0 1-1.414-1.414l4.657-4.657H4.414l4.657 4.657a1 1 0 1 1-1.414 1.414l-6.364-6.364Z"
|
||||||
// clipRule="evenodd"
|
fill="currentColor"
|
||||||
// d="M1.293 12.207a1 1 0 0 1 0-1.414l6.364-6.364A1 1 0 0 1 9.07 5.843L4.414 10.5h15.172l-4.657-4.657a1 1 0 0 1 1.414-1.414l6.364 6.364a1 1 0 0 1 0 1.414l-6.364 6.364a1 1 0 0 1-1.414-1.414l4.657-4.657H4.414l4.657 4.657a1 1 0 1 1-1.414 1.414l-6.364-6.364Z"
|
/>
|
||||||
// fill="currentColor"
|
),
|
||||||
// />
|
});
|
||||||
// ),
|
|
||||||
// });
|
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
|
import { StrictMode } from 'react';
|
||||||
|
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
import App from '@/App';
|
import App from '@/App';
|
||||||
import { ThemeProvider } from './theme/ThemeContext';
|
import { ColorModeProvider } from './components/ui/color-mode';
|
||||||
|
|
||||||
// Render the app
|
// Render the app
|
||||||
const rootElement = document.getElementById('root');
|
const rootElement = document.getElementById('root');
|
||||||
if (rootElement && !rootElement.innerHTML) {
|
if (rootElement && !rootElement.innerHTML) {
|
||||||
const root = ReactDOM.createRoot(rootElement);
|
const root = ReactDOM.createRoot(rootElement);
|
||||||
root.render(
|
root.render(
|
||||||
// <StrictMode>
|
<StrictMode>
|
||||||
<ThemeProvider>
|
<ColorModeProvider>
|
||||||
<App />
|
<App />
|
||||||
</ThemeProvider>
|
</ColorModeProvider>
|
||||||
// </StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { AudioPlayer } from '@/components/AudioPlayer';
|
import { AudioPlayer } from '@/components/AudioPlayer';
|
||||||
import { ErrorBoundary } from '@/errors/ErrorBoundary';
|
import { ErrorBoundary } from '@/errors/ErrorBoundary';
|
||||||
import { AppRoutes } from '@/scene/AppRoutes';
|
import { AppRoutes } from '@/scene/AppRoutes';
|
||||||
import { Text } from '@/ui';
|
|
||||||
import { ServiceContextProvider } from '@/service/ServiceContext';
|
import { ServiceContextProvider } from '@/service/ServiceContext';
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||||
import { MdEdit } from 'react-icons/md';
|
import { LuDisc3 } from 'react-icons/lu';
|
||||||
|
import { Md1kPlus, MdAdd, MdEdit, MdPlusOne } from 'react-icons/md';
|
||||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
@ -13,9 +14,8 @@ import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
|
|||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { useSpecificAlbum } from '@/service/Album';
|
import { useSpecificAlbum } from '@/service/Album';
|
||||||
import { useTracksOfAnAlbum } from '@/service/Track';
|
import { useTracksOfAnAlbum } from '@/service/Track';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
||||||
import { Button, Flex, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const AlbumDetailPage = () => {
|
export const AlbumDetailPage = () => {
|
||||||
const { albumId } = useParams();
|
const { albumId } = useParams();
|
||||||
@ -46,7 +46,7 @@ export const AlbumDetailPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar title="Album detail" />
|
<TopBar title="Album detail" />
|
||||||
<PageLayoutInfoCenter width="75%">
|
<PageLayoutInfoCenter>
|
||||||
Fail to load artist id: {albumId}
|
Fail to load artist id: {albumId}
|
||||||
</PageLayoutInfoCenter>
|
</PageLayoutInfoCenter>
|
||||||
</>
|
</>
|
||||||
@ -68,19 +68,17 @@ export const AlbumDetailPage = () => {
|
|||||||
data-testid="Album-detail-page_layout">
|
data-testid="Album-detail-page_layout">
|
||||||
<Flex
|
<Flex
|
||||||
direction="row"
|
direction="row"
|
||||||
|
width="80%"
|
||||||
|
marginX="auto"
|
||||||
|
padding="10px"
|
||||||
gap="10px"
|
gap="10px"
|
||||||
style={{
|
|
||||||
width: "80%",
|
|
||||||
margin: "0 auto",
|
|
||||||
padding: "10px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Covers
|
<Covers
|
||||||
data={dataAlbum?.covers}
|
data={dataAlbum?.covers}
|
||||||
// TODO: iconEmpty={LuDisc3}
|
iconEmpty={<LuDisc3 />}
|
||||||
slideshow
|
slideshow
|
||||||
/>
|
/>
|
||||||
<Flex direction="column" style={{ width: "80%", marginRight: "auto" }}>
|
<Flex direction="column" width="80%" marginRight="auto">
|
||||||
<Text fontSize="24px" fontWeight="bold">
|
<Text fontSize="24px" fontWeight="bold">
|
||||||
{dataAlbum?.name}
|
{dataAlbum?.name}
|
||||||
</Text>
|
</Text>
|
||||||
@ -95,27 +93,25 @@ export const AlbumDetailPage = () => {
|
|||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap={BASE_WRAP_SPACING} style={{
|
gap={BASE_WRAP_SPACING}
|
||||||
margin: "0 auto",
|
marginX="auto"
|
||||||
padding: "20px",
|
padding="20px"
|
||||||
width: "80%",
|
width="80%"
|
||||||
}}
|
data-testid="Album-detail-page_flex-list"
|
||||||
data-test-id="Album-detail-page_flex-list"
|
|
||||||
>
|
>
|
||||||
{tracksOnAnAlbum?.map((data) => (
|
{tracksOnAnAlbum?.map((data) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
//height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
minWidth: "100%",
|
as="button"
|
||||||
//height="60px"
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrackFull
|
<DisplayTrackFull
|
||||||
@ -124,15 +120,20 @@ export const AlbumDetailPage = () => {
|
|||||||
contextMenu={[
|
contextMenu={[
|
||||||
{
|
{
|
||||||
name: 'Edit',
|
name: 'Edit',
|
||||||
|
icon: <MdEdit />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(`/album/${albumId}/edit-track/${data.id}`);
|
navigate(`/album/${albumId}/edit-track/${data.id}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ name: 'Add Playlist', onClick: () => { } },
|
{
|
||||||
|
icon: <MdAdd />,
|
||||||
|
name: 'Add Playlist',
|
||||||
|
onClick: () => { }
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
data-testid="Album-detail-page_display-detail"
|
data-testid="Album-detail-page_display-detail"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -9,9 +9,9 @@ import { SearchInput } from '@/components/SearchInput';
|
|||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { DisplayAlbum } from '@/components/album/DisplayAlbum';
|
import { DisplayAlbum } from '@/components/album/DisplayAlbum';
|
||||||
import { useOrderedAlbums } from '@/service/Album';
|
import { useOrderedAlbums } from '@/service/Album';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { BASE_WRAP_WIDTH, BASE_WRAP_HEIGHT } from '@/constants/genericSpacing';
|
import { BASE_WRAP_SPACING, BASE_WRAP_WIDTH, BASE_WRAP_HEIGHT } from '@/constants/genericSpacing';
|
||||||
import { Button, Flex, HStack } from '@/ui';
|
import { Flex, HStack } from '@chakra-ui/react';
|
||||||
|
|
||||||
export const AlbumsPage = () => {
|
export const AlbumsPage = () => {
|
||||||
const [filterTitle, setFilterTitle] = useState<string | undefined>(undefined);
|
const [filterTitle, setFilterTitle] = useState<string | undefined>(undefined);
|
||||||
@ -25,7 +25,7 @@ export const AlbumsPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar title="All Albums" />
|
<TopBar title="All Albums" />
|
||||||
<PageLayoutInfoCenter width="75%">No Album available</PageLayoutInfoCenter>
|
<PageLayoutInfoCenter>No Album available</PageLayoutInfoCenter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -36,31 +36,29 @@ export const AlbumsPage = () => {
|
|||||||
<SearchInput onChange={setFilterTitle} />
|
<SearchInput onChange={setFilterTitle} />
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<HStack style={{ flexWrap: "wrap", /*spacing={BASE_WRAP_SPACING}*/ margin: "0 auto", padding: "20px", justifyContent: "center" }}>
|
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||||
{dataAlbums.map((data) => (
|
{dataAlbums.map((data) => (
|
||||||
<Button
|
<Flex align="flex-start"
|
||||||
|
width={BASE_WRAP_WIDTH}
|
||||||
|
height={BASE_WRAP_HEIGHT}
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
width: BASE_WRAP_WIDTH,
|
as="button"
|
||||||
height: BASE_WRAP_HEIGHT,
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover={
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorThemeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
alignContent: "flex-start",
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data.id)}
|
onClick={() => onSelectItem(data.id)}
|
||||||
>
|
>
|
||||||
<DisplayAlbum dataAlbum={data} />
|
<DisplayAlbum dataAlbum={data} />
|
||||||
</Button>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</HStack>
|
</HStack>
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</PageLayout >
|
</PageLayout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||||
import { MdEdit } from 'react-icons/md';
|
import { LuDisc3, LuUser } from 'react-icons/lu';
|
||||||
|
import { MdAdd, MdEdit, MdPerson } from 'react-icons/md';
|
||||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Covers } from '@/components/Cover';
|
import { Covers } from '@/components/Cover';
|
||||||
@ -14,8 +15,7 @@ import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
|||||||
import { useSpecificAlbum } from '@/service/Album';
|
import { useSpecificAlbum } from '@/service/Album';
|
||||||
import { useSpecificArtist } from '@/service/Artist';
|
import { useSpecificArtist } from '@/service/Artist';
|
||||||
import { useTracksOfAnAlbum } from '@/service/Track';
|
import { useTracksOfAnAlbum } from '@/service/Track';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Button, Flex, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const ArtistAlbumDetailPage = () => {
|
export const ArtistAlbumDetailPage = () => {
|
||||||
const { artistId, albumId } = useParams();
|
const { artistId, albumId } = useParams();
|
||||||
@ -60,19 +60,14 @@ export const ArtistAlbumDetailPage = () => {
|
|||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
{...BUTTON_TOP_BAR_PROPERTY}
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
|
marginRight="auto"
|
||||||
style={{
|
|
||||||
marginRight: "auto"
|
|
||||||
}}
|
|
||||||
onClick={() => navigate(`/artist/${dataArtist.id}`)}
|
onClick={() => navigate(`/artist/${dataArtist.id}`)}
|
||||||
>
|
>
|
||||||
<Covers
|
<Covers
|
||||||
data={dataArtist?.covers}
|
data={dataArtist?.covers}
|
||||||
size="35px"
|
size="35px"
|
||||||
style={{
|
borderRadius="full"
|
||||||
borderRadius: "full",
|
iconEmpty={<MdPerson />}
|
||||||
}}
|
|
||||||
// TODO: iconEmpty={MdPerson}
|
|
||||||
/>
|
/>
|
||||||
<Text fontSize="24px" fontWeight="bold">
|
<Text fontSize="24px" fontWeight="bold">
|
||||||
{dataArtist?.name}
|
{dataArtist?.name}
|
||||||
@ -92,20 +87,17 @@ export const ArtistAlbumDetailPage = () => {
|
|||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Flex
|
<Flex
|
||||||
direction="row"
|
direction="row"
|
||||||
style={{
|
width="80%"
|
||||||
width: "80%",
|
marginX="auto"
|
||||||
margin: "0 auto",
|
padding="10px"
|
||||||
padding: "10px",
|
|
||||||
}}
|
|
||||||
gap="10px"
|
gap="10px"
|
||||||
>
|
>
|
||||||
<Covers
|
<Covers
|
||||||
data={dataAlbum?.covers}
|
data={dataAlbum?.covers}
|
||||||
// TODO: iconEmpty={LuDisc3}
|
iconEmpty={<LuDisc3 />}
|
||||||
slideshow
|
slideshow
|
||||||
/>
|
/>
|
||||||
<Flex direction="column"
|
<Flex direction="column" width="80%" marginRight="auto">
|
||||||
style={{ width: "80%", marginRight: "auto" }}>
|
|
||||||
<Text fontSize="24px" fontWeight="bold">
|
<Text fontSize="24px" fontWeight="bold">
|
||||||
{dataAlbum?.name}
|
{dataAlbum?.name}
|
||||||
</Text>
|
</Text>
|
||||||
@ -121,28 +113,22 @@ export const ArtistAlbumDetailPage = () => {
|
|||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap="20px"
|
gap="20px"
|
||||||
|
marginX="auto"
|
||||||
style={{
|
padding="20px"
|
||||||
margin: "0 auto",
|
width="80%"
|
||||||
padding: "20px",
|
|
||||||
width: "80%",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{tracksOnAnAlbum?.map((data) => (
|
{tracksOnAnAlbum?.map((data) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
|
padding="5px"
|
||||||
style={{
|
_hover={{
|
||||||
minWidth: "100%",
|
boxShadow: 'outline-over',
|
||||||
height: "60px",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
border: "1px",
|
|
||||||
borderColor: "brand.900",
|
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrack
|
<DisplayTrack
|
||||||
@ -151,16 +137,21 @@ export const ArtistAlbumDetailPage = () => {
|
|||||||
contextMenu={[
|
contextMenu={[
|
||||||
{
|
{
|
||||||
name: 'Edit',
|
name: 'Edit',
|
||||||
|
icon: <MdEdit />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(
|
navigate(
|
||||||
`/artist/${artistIdInt}/album/${albumId}/edit-track/${data.id}`
|
`/artist/${artistIdInt}/album/${albumId}/edit-track/${data.id}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ name: 'Add Playlist', onClick: () => { } },
|
{
|
||||||
|
icon: <MdAdd />,
|
||||||
|
name: 'Add Playlist',
|
||||||
|
onClick: () => { }
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
import { Button, Flex, Text, HStack } from '@chakra-ui/react';
|
||||||
|
import { LuUser } from 'react-icons/lu';
|
||||||
import { MdEdit, MdGroup } from 'react-icons/md';
|
import { MdEdit, MdGroup } from 'react-icons/md';
|
||||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
@ -11,9 +12,8 @@ import { DisplayAlbumId } from '@/components/album/DisplayAlbumId';
|
|||||||
import { ArtistEditPopUp } from '@/components/popup/ArtistEditPopUp';
|
import { ArtistEditPopUp } from '@/components/popup/ArtistEditPopUp';
|
||||||
import { useSpecificArtist } from '@/service/Artist';
|
import { useSpecificArtist } from '@/service/Artist';
|
||||||
import { useAlbumIdsOfAnArtist } from '@/service/Track';
|
import { useAlbumIdsOfAnArtist } from '@/service/Track';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { BASE_WRAP_HEIGHT, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
import { BASE_WRAP_HEIGHT, BASE_WRAP_SPACING, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
||||||
import { Button, Flex, HStack, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const ArtistDetailPage = () => {
|
export const ArtistDetailPage = () => {
|
||||||
const { artistId } = useParams();
|
const { artistId } = useParams();
|
||||||
@ -41,7 +41,7 @@ export const ArtistDetailPage = () => {
|
|||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
{...BUTTON_TOP_BAR_PROPERTY}
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
style={{ marginRight: "auto" }}
|
marginRight="auto"
|
||||||
onClick={() => navigate(`/artist/all`)}
|
onClick={() => navigate(`/artist/all`)}
|
||||||
>
|
>
|
||||||
<MdGroup height="full" />
|
<MdGroup height="full" />
|
||||||
@ -64,13 +64,13 @@ export const ArtistDetailPage = () => {
|
|||||||
<Flex
|
<Flex
|
||||||
direction="row"
|
direction="row"
|
||||||
width="80%"
|
width="80%"
|
||||||
margin="0 auto"
|
marginX="auto"
|
||||||
padding="10px"
|
padding="10px"
|
||||||
gap="10px"
|
gap="10px"
|
||||||
>
|
>
|
||||||
<Covers
|
<Covers
|
||||||
data={dataArtist?.covers}
|
data={dataArtist?.covers}
|
||||||
// TODO: iconEmpty={LuUser}
|
iconEmpty={<LuUser />}
|
||||||
slideshow
|
slideshow
|
||||||
/>
|
/>
|
||||||
<Flex direction="column" width="80%" marginRight="auto">
|
<Flex direction="column" width="80%" marginRight="auto">
|
||||||
@ -88,27 +88,25 @@ export const ArtistDetailPage = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<HStack style={{ flexWrap: "wrap", /*spacing={BASE_WRAP_SPACING}*/ margin: "0 auto", padding: "20px", justifyContent: "center" }}>
|
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||||
{albumIdsOfAnArtist?.map((data) => (
|
{albumIdsOfAnArtist?.map((data) => (
|
||||||
<Button
|
<Flex align="flex-start"
|
||||||
|
width={BASE_WRAP_WIDTH}
|
||||||
|
height={BASE_WRAP_HEIGHT}
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data}
|
key={data}
|
||||||
style={{
|
padding="5px"
|
||||||
alignContent: "flex-start",
|
as="button"
|
||||||
width: BASE_WRAP_WIDTH,
|
_hover={{
|
||||||
height: BASE_WRAP_HEIGHT,
|
boxShadow: 'outline-over',
|
||||||
border: "1px",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
borderColor: "brand.900",
|
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover={
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorThemeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data)}
|
onClick={() => onSelectItem(data)}
|
||||||
>
|
>
|
||||||
<DisplayAlbumId id={data} key={data} />
|
<DisplayAlbumId id={data} key={data} />
|
||||||
</Button>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</HStack>
|
</HStack>
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Button, Flex, Text, Tooltip, HStack, Span } from '@chakra-ui/react';
|
||||||
|
import { LuUser } from 'react-icons/lu';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { Artist, Track } from '@/back-api';
|
import { Artist, Track } from '@/back-api';
|
||||||
@ -9,14 +11,13 @@ import { PageLayout } from '@/components/Layout/PageLayout';
|
|||||||
import { SearchInput } from '@/components/SearchInput';
|
import { SearchInput } from '@/components/SearchInput';
|
||||||
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { useOrderedArtists } from '@/service/Artist';
|
import { useOrderedArtists } from '@/service/Artist';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { BASE_WRAP_HEIGHT, BASE_WRAP_ICON_SIZE, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
import { BASE_WRAP_HEIGHT, BASE_WRAP_ICON_SIZE, BASE_WRAP_SPACING, BASE_WRAP_WIDTH } from '@/constants/genericSpacing';
|
||||||
import { MdOutlineForkRight } from 'react-icons/md';
|
import { MdOutlineForkRight } from 'react-icons/md';
|
||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { shuffleArray } from '@/utils/arrayTools';
|
import { shuffleArray } from '@/utils/arrayTools';
|
||||||
import { useTrackService } from '@/service/Track';
|
import { useTrackService } from '@/service/Track';
|
||||||
import { DataTools, TypeCheck } from '@/utils/data-tools';
|
import { DataTools, TypeCheck } from '@/utils/data-tools';
|
||||||
import { Button, Flex, HStack, Span, Text } from '@/ui';
|
|
||||||
const LIMIT_RANDOM_VALUES = 25;
|
const LIMIT_RANDOM_VALUES = 25;
|
||||||
|
|
||||||
export const ArtistsPage = () => {
|
export const ArtistsPage = () => {
|
||||||
@ -56,27 +57,27 @@ export const ArtistsPage = () => {
|
|||||||
<>
|
<>
|
||||||
<TopBar title="All artists">
|
<TopBar title="All artists">
|
||||||
<SearchInput onChange={setFilterName} />
|
<SearchInput onChange={setFilterName} />
|
||||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}>
|
<Tooltip.Root aria-label="Random play">
|
||||||
<MdOutlineForkRight />
|
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}>
|
||||||
</Button>
|
<MdOutlineForkRight style={{ width: "100%", height: "100%" }} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip.Root>
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<HStack style={{ flexWrap: "wrap", /*spacing={BASE_WRAP_SPACING}*/ margin: "0 auto", padding: "20px", alignContent: "center" }}>
|
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||||
{dataArtist?.map((data) => (
|
{dataArtist?.map((data) => (
|
||||||
<Button
|
<Flex align="flex-start"
|
||||||
|
width={BASE_WRAP_WIDTH}
|
||||||
|
height={BASE_WRAP_HEIGHT}
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
width: BASE_WRAP_WIDTH,
|
as="button"
|
||||||
height: BASE_WRAP_HEIGHT,
|
_hover={{
|
||||||
alignContent: "flex-start",
|
boxShadow: 'outline-over',
|
||||||
border: "1px",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
borderColor: "brand.900",
|
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover:{
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorThemeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data)}
|
onClick={() => onSelectItem(data)}
|
||||||
>
|
>
|
||||||
@ -84,8 +85,8 @@ export const ArtistsPage = () => {
|
|||||||
<Covers
|
<Covers
|
||||||
data={data.covers}
|
data={data.covers}
|
||||||
size={BASE_WRAP_ICON_SIZE}
|
size={BASE_WRAP_ICON_SIZE}
|
||||||
style={{ height: "full" }}
|
height="full"
|
||||||
// iconEmpty={LuUser}
|
iconEmpty={<LuUser />}
|
||||||
/>
|
/>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
@ -93,20 +94,18 @@ export const ArtistsPage = () => {
|
|||||||
maxWidth="150px"
|
maxWidth="150px"
|
||||||
height="full"
|
height="full"
|
||||||
paddingLeft="5px"
|
paddingLeft="5px"
|
||||||
style={{ overflowX: "hidden" }}
|
overflowX="hidden"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
/*align="left"*/
|
textAlign="left"
|
||||||
/*noOfLines={[1, 2]}*/
|
/*noOfLines={[1, 2]}*/
|
||||||
>
|
>
|
||||||
<Span
|
<Span
|
||||||
fontSize="20px"
|
fontSize="20px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
userSelect="none"
|
userSelect="none"
|
||||||
style={{
|
marginRight="auto"
|
||||||
marginRight: "auto",
|
overflow="hidden"
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{data.name}
|
{data.name}
|
||||||
</Span>
|
</Span>
|
||||||
@ -114,11 +113,11 @@ export const ArtistsPage = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Button>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</HStack>
|
</HStack>
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</PageLayout>
|
</PageLayout >
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||||
|
import { LuDisc3 } from 'react-icons/lu';
|
||||||
import { MdEdit } from 'react-icons/md';
|
import { MdEdit } from 'react-icons/md';
|
||||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
@ -9,13 +10,13 @@ import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
|||||||
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { GenderEditPopUp } from '@/components/popup/GenderEditPopUp';
|
import { GenderEditPopUp } from '@/components/popup/GenderEditPopUp';
|
||||||
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||||
|
import { DisplayTrack } from '@/components/track/DisplayTrack';
|
||||||
import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
|
import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
|
||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { useSpecificGender } from '@/service/Gender';
|
import { useSpecificGender } from '@/service/Gender';
|
||||||
import { useTracksOfAGender } from '@/service/Track';
|
import { useTracksOfAGender } from '@/service/Track';
|
||||||
//import { useTracksOfAGender } from '@/service/Track';
|
//import { useTracksOfAGender } from '@/service/Track';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Button, Flex, Text } from '@/ui';
|
|
||||||
|
|
||||||
export const GenderDetailPage = () => {
|
export const GenderDetailPage = () => {
|
||||||
const { genderId } = useParams();
|
const { genderId } = useParams();
|
||||||
@ -67,19 +68,17 @@ export const GenderDetailPage = () => {
|
|||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Flex
|
<Flex
|
||||||
direction="row"
|
direction="row"
|
||||||
|
width="80%"
|
||||||
|
marginX="auto"
|
||||||
|
padding="10px"
|
||||||
gap="10px"
|
gap="10px"
|
||||||
style={{
|
|
||||||
width: "80%",
|
|
||||||
margin: "0 auto",
|
|
||||||
padding: "10px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Covers
|
<Covers
|
||||||
data={dataGender?.covers}
|
data={dataGender?.covers}
|
||||||
// TODO: iconEmpty={LuDisc3}
|
iconEmpty={<LuDisc3 />}
|
||||||
slideshow
|
slideshow
|
||||||
/>
|
/>
|
||||||
<Flex direction="column" style={{ width: "80%", marginRight: "auto" }}>
|
<Flex direction="column" width="80%" marginRight="auto">
|
||||||
<Text fontSize="24px" fontWeight="bold">
|
<Text fontSize="24px" fontWeight="bold">
|
||||||
{dataGender?.name}
|
{dataGender?.name}
|
||||||
</Text>
|
</Text>
|
||||||
@ -92,26 +91,23 @@ export const GenderDetailPage = () => {
|
|||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap="20px"
|
gap="20px"
|
||||||
style={{
|
marginX="auto"
|
||||||
margin: "0 auto",
|
padding="20px"
|
||||||
padding: "20px",
|
width="80%"
|
||||||
width: "80%",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{tracksOnAGender?.map((data) => (
|
{tracksOnAGender?.map((data) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
//height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
minWidth: "100%",
|
as="button"
|
||||||
//height="60px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrackFull
|
<DisplayTrackFull
|
||||||
@ -127,7 +123,7 @@ export const GenderDetailPage = () => {
|
|||||||
{ name: 'Add Playlist', onClick: () => { } },
|
{ name: 'Add Playlist', onClick: () => { } },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -135,7 +131,7 @@ export const GenderDetailPage = () => {
|
|||||||
<Route path="edit-track/:trackId" element={<TrackEditPopUp />} />
|
<Route path="edit-track/:trackId" element={<TrackEditPopUp />} />
|
||||||
<Route path="edit-gender/:genderId" element={<GenderEditPopUp />} />
|
<Route path="edit-gender/:genderId" element={<GenderEditPopUp />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</PageLayout>
|
</PageLayout >
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,8 +9,8 @@ import { SearchInput } from '@/components/SearchInput';
|
|||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { DisplayGender } from '@/components/gender/DisplayGender';
|
import { DisplayGender } from '@/components/gender/DisplayGender';
|
||||||
import { useOrderedGenders } from '@/service/Gender';
|
import { useOrderedGenders } from '@/service/Gender';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Flex, HStack } from '@/ui';
|
import { Flex, HStack } from '@chakra-ui/react';
|
||||||
|
|
||||||
export const GendersPage = () => {
|
export const GendersPage = () => {
|
||||||
const [filterTitle, setFilterTitle] = useState<string | undefined>(undefined);
|
const [filterTitle, setFilterTitle] = useState<string | undefined>(undefined);
|
||||||
@ -35,24 +35,20 @@ export const GendersPage = () => {
|
|||||||
<SearchInput onChange={setFilterTitle} />
|
<SearchInput onChange={setFilterTitle} />
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<HStack style={{
|
<HStack wrap="wrap" gap="20px" marginX="auto" padding="20px" justify="center">
|
||||||
// wrap: "wrap",
|
|
||||||
/*spacing="20px"*/ margin: "0 auto 0 auto", padding: "20px", justifyContent: "center"
|
|
||||||
}}>
|
|
||||||
{dataGenders.map((data) => (
|
{dataGenders.map((data) => (
|
||||||
<Flex align="flex-start"
|
<Flex align="flex-start"
|
||||||
|
width="270px"
|
||||||
|
height="120px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
width: "270px",
|
as="button"
|
||||||
height: "120px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
//backgroundColor:{useColorModeValue('#FFFFFF88', '#00000088')},
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data.id)}
|
onClick={() => onSelectItem(data.id)}
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
Table,
|
||||||
|
Text,
|
||||||
|
Button,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { LuTrash } from 'react-icons/lu';
|
import { LuTrash } from 'react-icons/lu';
|
||||||
import { MdCloudUpload } from 'react-icons/md';
|
import { MdCloudUpload } from 'react-icons/md';
|
||||||
|
|
||||||
@ -25,7 +32,6 @@ import { useGenderService, useOrderedGenders } from '@/service/Gender';
|
|||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
import { useTrackService } from '@/service/Track';
|
import { useTrackService } from '@/service/Track';
|
||||||
import { isNullOrUndefined } from '@/utils/validator';
|
import { isNullOrUndefined } from '@/utils/validator';
|
||||||
import { Button, Flex, Table, Text } from '@/ui';
|
|
||||||
|
|
||||||
export class ElementList {
|
export class ElementList {
|
||||||
constructor(
|
constructor(
|
||||||
@ -115,7 +121,7 @@ export const AddPage = () => {
|
|||||||
updateNeedSend();
|
updateNeedSend();
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeElementFromList = (data: FileParsedElement): void => {
|
const removeElementFromList = (data: FileParsedElement, value: any): void => {
|
||||||
const parsedElementTmp = [...parsedElement];
|
const parsedElementTmp = [...parsedElement];
|
||||||
for (let iii = 0; iii < parsedElementTmp.length; iii++) {
|
for (let iii = 0; iii < parsedElementTmp.length; iii++) {
|
||||||
if (parsedElementTmp[iii] === data) {
|
if (parsedElementTmp[iii] === data) {
|
||||||
@ -414,17 +420,15 @@ export const AddPage = () => {
|
|||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
|
width="80%"
|
||||||
|
marginX="auto"
|
||||||
|
padding="10px"
|
||||||
gap="10px"
|
gap="10px"
|
||||||
style={{
|
|
||||||
width: "80%",
|
|
||||||
margin: "0 auto 0 auto",
|
|
||||||
padding: "10px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Flex direction="column" style={{ width: "full" }}>
|
<Flex direction="column" width="full">
|
||||||
<Flex>
|
<Flex>
|
||||||
<Text>format:</Text>
|
<Text flex={1}>format:</Text>
|
||||||
<Text>
|
<Text flex={4}>
|
||||||
The format of the media permit to automatic find meta-data:
|
The format of the media permit to automatic find meta-data:
|
||||||
<br />
|
<br />
|
||||||
Artist~album#idTrack-my name of my media.webm
|
Artist~album#idTrack-my name of my media.webm
|
||||||
@ -433,15 +437,15 @@ export const AddPage = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Text>Media:</Text>
|
<Text flex={1}>Media:</Text>
|
||||||
{/* <Input
|
<Input
|
||||||
flex={4}
|
flex={4}
|
||||||
type="file"
|
type="file"
|
||||||
placeholder="Select a media file"
|
placeholder="Select a media file"
|
||||||
accept=".webm"
|
accept=".webm"
|
||||||
multiple
|
multiple
|
||||||
onChange={onChangeFile}
|
onChange={onChangeFile}
|
||||||
/> */}
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
{parsedElement && parsedElement.length !== 0 && (
|
{parsedElement && parsedElement.length !== 0 && (
|
||||||
@ -474,12 +478,14 @@ export const AddPage = () => {
|
|||||||
suggestion={suggestedAlbum}
|
suggestion={suggestedAlbum}
|
||||||
/>
|
/>
|
||||||
<Table.Root
|
<Table.Root
|
||||||
|
colorPalette="striped"
|
||||||
|
colorScheme="teal"
|
||||||
background="gray.700"
|
background="gray.700"
|
||||||
>
|
>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader>track ID</Table.ColumnHeader>
|
<Table.ColumnHeader>track ID</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader style={{ width: "full" }}>Title</Table.ColumnHeader>
|
<Table.ColumnHeader width="full">Title</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>actions</Table.ColumnHeader>
|
<Table.ColumnHeader>actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
@ -487,7 +493,7 @@ export const AddPage = () => {
|
|||||||
{parsedElement.map((data) => (
|
{parsedElement.map((data) => (
|
||||||
<Table.Row key={data.uniqueId}>
|
<Table.Row key={data.uniqueId}>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
{/* <Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
pattern="[0-9]{0-4}"
|
pattern="[0-9]{0-4}"
|
||||||
placeholder="e?"
|
placeholder="e?"
|
||||||
@ -498,10 +504,10 @@ export const AddPage = () => {
|
|||||||
? 'darkred'
|
? 'darkred'
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/> */}
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
{/* <Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name of the Media"
|
placeholder="Name of the Media"
|
||||||
value={data.title}
|
value={data.title}
|
||||||
@ -509,11 +515,11 @@ export const AddPage = () => {
|
|||||||
backgroundColor={
|
backgroundColor={
|
||||||
data.title === '' ? 'darkred' : undefined
|
data.title === '' ? 'darkred' : undefined
|
||||||
}
|
}
|
||||||
/> */}
|
/>
|
||||||
{data.nameDetected === true && (
|
{data.nameDetected === true && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
<Text style={{ backgroundColor: "red" }}>
|
<Text as="span" color="@danger">
|
||||||
^^^This title already exist !!!
|
^^^This title already exist !!!
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
@ -521,8 +527,8 @@ export const AddPage = () => {
|
|||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={(e) =>
|
||||||
removeElementFromList(data)
|
removeElementFromList(data, e.target)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<LuTrash /> Remove
|
<LuTrash /> Remove
|
||||||
@ -532,14 +538,13 @@ export const AddPage = () => {
|
|||||||
))}
|
))}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table.Root>
|
</Table.Root>
|
||||||
<Flex style={{ margin: "15px 0 15px 0" }}>
|
<Flex marginY="15px">
|
||||||
<Button
|
<Button
|
||||||
//theme="@primary"
|
colorPalette="brand"
|
||||||
onClick={sendFile}
|
onClick={sendFile}
|
||||||
style={{
|
disabled={!needSend}
|
||||||
//disabled:!needSend,
|
marginLeft="auto"
|
||||||
margin: "0 auto 0 30px"
|
marginRight="30px"
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<MdCloudUpload /> Upload
|
<MdCloudUpload /> Upload
|
||||||
</Button>
|
</Button>
|
||||||
@ -549,16 +554,14 @@ export const AddPage = () => {
|
|||||||
|
|
||||||
{listFileInBdd && (
|
{listFileInBdd && (
|
||||||
<Table.Root
|
<Table.Root
|
||||||
//fontPalette="striped"
|
fontPalette="striped"
|
||||||
//colorScheme="teal"
|
colorScheme="teal"
|
||||||
style={{
|
background="gray.700"
|
||||||
background: "gray.700"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader>track ID</Table.ColumnHeader>
|
<Table.ColumnHeader>track ID</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader style={{ width: "full" }}>Title</Table.ColumnHeader>
|
<Table.ColumnHeader width="full">Title</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>actions</Table.ColumnHeader>
|
<Table.ColumnHeader>actions</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
@ -594,13 +597,13 @@ export const AddPage = () => {
|
|||||||
<>
|
<>
|
||||||
<Text fontSize="30px">Rejected:</Text>
|
<Text fontSize="30px">Rejected:</Text>
|
||||||
<Table.Root
|
<Table.Root
|
||||||
//colorPalette="striped"
|
colorPalette="striped"
|
||||||
//colorScheme="teal"
|
colorScheme="teal"
|
||||||
style={{ background: "gray.700" }}
|
background="gray.700"
|
||||||
>
|
>
|
||||||
<Table.Header>
|
<Table.Header>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.ColumnHeader style={{ maxWidth: "80%" }}>file</Table.ColumnHeader>
|
<Table.ColumnHeader maxWidth="80%">file</Table.ColumnHeader>
|
||||||
<Table.ColumnHeader>Reason</Table.ColumnHeader>
|
<Table.ColumnHeader>Reason</Table.ColumnHeader>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ReactElement } from 'react';
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
|
import { Center, Flex, HStack, Text } from '@chakra-ui/react';
|
||||||
import { LuCrown, LuDisc3, LuEar, LuFileAudio } from 'react-icons/lu';
|
import { LuCrown, LuDisc3, LuEar, LuFileAudio } from 'react-icons/lu';
|
||||||
import { MdGroup } from 'react-icons/md';
|
import { MdGroup } from 'react-icons/md';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
@ -7,8 +8,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
|
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Div, Flex, HStack, Text } from '@/ui';
|
|
||||||
|
|
||||||
type HomeListType = {
|
type HomeListType = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -58,43 +58,40 @@ export const HomePage = () => {
|
|||||||
<>
|
<>
|
||||||
<TopBar title="Home" />
|
<TopBar title="Home" />
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<HStack style={{ flexWrap: "wrap", /*spacing="20px"*/ margin: "0 auto", padding: "20px", justify: "center" }}>
|
<HStack wrap="wrap" gap="20px" marginX="auto" padding="20px" justify="center">
|
||||||
{homeList.map((data) => (
|
{homeList.map((data) => (
|
||||||
<Flex align="flex-start"
|
<Flex align="flex-start"
|
||||||
|
width="200px"
|
||||||
|
height="190px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
width: "200px",
|
as="button"
|
||||||
height: "190px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover:{
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorThemeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data)}
|
onClick={() => onSelectItem(data)}
|
||||||
>
|
>
|
||||||
<Flex direction="column" style={{ width: "full", height: "full" }}>
|
<Flex direction="column" width="full" height="full">
|
||||||
<Div style={{ alignContent: "center", height: "full" }}>{data.icon}</Div>
|
<Center height="full">{data.icon}</Center>
|
||||||
<Div style={{ alignContent: "center", height: "full" }}>
|
<Center>
|
||||||
<Text
|
<Text
|
||||||
fontSize="25px"
|
fontSize="25px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
style={{
|
textTransform="uppercase"
|
||||||
textTransform: "uppercase",
|
userSelect="none"
|
||||||
userSelect: "none",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{data.name}
|
{data.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Div>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
))}
|
))}
|
||||||
</HStack>
|
</HStack>
|
||||||
</PageLayout >
|
</PageLayout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
|
import { Box, Flex, Text } from '@chakra-ui/react';
|
||||||
import { Route, Routes, useNavigate } from 'react-router-dom';
|
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp';
|
import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp';
|
||||||
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
||||||
import { DisplayTrackFullId } from '@/components/track/DisplayTrackFullId';
|
import { DisplayTrackFullId } from '@/components/track/DisplayTrackFullId';
|
||||||
import { Flex } from '@/ui';
|
import { MdCleanHands, MdClear, MdDryCleaning, MdEdit, MdKeyboardDoubleArrowDown, MdKeyboardDoubleArrowUp, MdLeakRemove, MdRemove, MdRemoveFromQueue, MdRemoveShoppingCart } from 'react-icons/md';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { LuTrash2 } from 'react-icons/lu';
|
||||||
|
|
||||||
export const OnAirPage = () => {
|
export const OnAirPage = () => {
|
||||||
const { playInList } = useActivePlaylistService();
|
const { playInList } = useActivePlaylistService();
|
||||||
@ -53,9 +56,39 @@ export const OnAirPage = () => {
|
|||||||
setNewPlaylist(newList);
|
setNewPlaylist(newList);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const removePreviousTrack = (index: number) => {
|
||||||
|
if (!playTrackList) {
|
||||||
|
console.log('Fail to remove element of on air...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newList = playTrackList.slice(index);
|
||||||
|
let newPlayed = trackOffset;
|
||||||
|
if (newPlayed != undefined) {
|
||||||
|
if (index < newPlayed) {
|
||||||
|
newPlayed = newPlayed - index;
|
||||||
|
} else {
|
||||||
|
newPlayed = 0;
|
||||||
|
}
|
||||||
|
playInList(newPlayed, newList);
|
||||||
|
} else {
|
||||||
|
setNewPlaylist(newList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const removeNextTrack = (index: number) => {
|
||||||
|
if (!playTrackList) {
|
||||||
|
console.log('Fail to remove element of on air...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newList = playTrackList.slice(0, index + 1);
|
||||||
|
playInList(trackOffset, newList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const clean = () => {
|
||||||
|
setNewPlaylist([]);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`playTrackList = ${JSON.stringify(playTrackList, null, 2)}`);
|
console.log(`playTrackList = ${JSON.stringify(playTrackList, null, 2)}`);
|
||||||
if (!playTrackList) {
|
if (!playTrackList || playTrackList.length == 0) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar title="Album detail" />
|
<TopBar title="Album detail" />
|
||||||
@ -68,32 +101,35 @@ export const OnAirPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar title="On Air ...">
|
<TopBar title="On Air ...">
|
||||||
|
<Button
|
||||||
|
{...BUTTON_TOP_BAR_PROPERTY}
|
||||||
|
onClick={clean}
|
||||||
|
>
|
||||||
|
<LuTrash2 />
|
||||||
|
</Button>
|
||||||
</TopBar>
|
</TopBar>
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap={BASE_WRAP_SPACING}
|
gap={BASE_WRAP_SPACING}
|
||||||
style={{
|
marginX="auto"
|
||||||
margin: "0 auto",
|
padding="20px"
|
||||||
padding: "20px",
|
width="80%"
|
||||||
width: "80%",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{!playTrackList && <>No playing</>}
|
{!playTrackList && <>No playing</>}
|
||||||
{playTrackList && playTrackList?.map((data) => (
|
{playTrackList && playTrackList?.map((data, index) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
//height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data}
|
key={data}
|
||||||
style={{
|
padding="5px"
|
||||||
minWidth: "100%",
|
as="button"
|
||||||
//height:"60px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrackFullId
|
<DisplayTrackFullId
|
||||||
@ -102,19 +138,35 @@ export const OnAirPage = () => {
|
|||||||
contextMenu={[
|
contextMenu={[
|
||||||
{
|
{
|
||||||
name: 'Edit',
|
name: 'Edit',
|
||||||
|
icon: <MdEdit />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(`edit-track/${data}`);
|
navigate(`edit-track/${data}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Remove from playlist',
|
name: 'Remove previous',
|
||||||
|
icon: <MdKeyboardDoubleArrowUp />,
|
||||||
|
onClick: () => {
|
||||||
|
removePreviousTrack(index);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove',
|
||||||
|
icon: <MdRemove />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
removeTrack(data);
|
removeTrack(data);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove After',
|
||||||
|
icon: <MdKeyboardDoubleArrowDown />,
|
||||||
|
onClick: () => {
|
||||||
|
removeNextTrack(index);
|
||||||
|
},
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { Center, Heading, Image, Text } from '@chakra-ui/react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import avatar_generic from '@/assets/images/avatar_generic.svg';
|
import avatar_generic from '@/assets/images/avatar_generic.svg';
|
||||||
@ -10,8 +11,6 @@ import { SessionState } from '@/service/SessionState';
|
|||||||
import { useSessionService } from '@/service/session';
|
import { useSessionService } from '@/service/session';
|
||||||
import { b64_to_utf8 } from '@/utils/sso';
|
import { b64_to_utf8 } from '@/utils/sso';
|
||||||
|
|
||||||
import { Flex, Text, Image } from '@/ui';
|
|
||||||
|
|
||||||
export const SSOPage = () => {
|
export const SSOPage = () => {
|
||||||
const { data, keepConnected, token } = useParams();
|
const { data, keepConnected, token } = useParams();
|
||||||
console.log(`- data: ${data}`);
|
console.log(`- data: ${data}`);
|
||||||
@ -48,11 +47,13 @@ export const SSOPage = () => {
|
|||||||
<>
|
<>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
<PageLayoutInfoCenter width="35%" gap="15px">
|
<PageLayoutInfoCenter width="35%" gap="15px">
|
||||||
<Text>LOGIN (after SSO)</Text>
|
<Center w="full">
|
||||||
|
<Heading size="xl">LOGIN (after SSO) </Heading>
|
||||||
|
</Center>
|
||||||
|
|
||||||
<Flex width="full">
|
<Center w="full">
|
||||||
<Image href={avatar_generic} boxSize="150px" style={{ borderRadius: "full" }} />
|
<Image src={avatar_generic} boxSize="150px" borderRadius="full" />
|
||||||
</Flex>
|
</Center>
|
||||||
{token === '__CANCEL__' && (
|
{token === '__CANCEL__' && (
|
||||||
<Text>
|
<Text>
|
||||||
<b>ERROR: </b> Request cancel of connection !
|
<b>ERROR: </b> Request cancel of connection !
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
|
import { Flex, Text, HStack } from '@chakra-ui/react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||||
import { TopBar } from '@/components/TopBar/TopBar';
|
import { TopBar } from '@/components/TopBar/TopBar';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { DataTools, TypeCheck } from '@/utils/data-tools';
|
||||||
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
|
|
||||||
export const alphabet = [
|
export const alphabet = [
|
||||||
'a',
|
'a',
|
||||||
@ -50,13 +54,13 @@ export const TrackSelectionPage = () => {
|
|||||||
height="75px"
|
height="75px"
|
||||||
border="1px"
|
border="1px"
|
||||||
borderColor="brand.900"
|
borderColor="brand.900"
|
||||||
backgroundColor={useColorThemeValue('#FFFFFF88', '#00000088')}
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data}
|
key={data}
|
||||||
padding="5px"
|
padding="5px"
|
||||||
as="button"
|
as="button"
|
||||||
_hover={{
|
_hover={{
|
||||||
boxShadow: 'outline-over',
|
boxShadow: 'outline-over',
|
||||||
bgColor: useColorThemeValue('#FFFFFFF7', '#000000F7'),
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
}}
|
}}
|
||||||
onClick={() => onSelectItem(data)}
|
onClick={() => onSelectItem(data)}
|
||||||
>
|
>
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { ReactElement } from 'react';
|
||||||
|
|
||||||
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||||
@ -8,8 +11,7 @@ import { DisplayTrackSkeleton } from '@/components/track/DisplayTrackSkeleton';
|
|||||||
import { alphabet } from '@/scene/track/TrackSelectionPage';
|
import { alphabet } from '@/scene/track/TrackSelectionPage';
|
||||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||||
import { useTracksWithStartName } from '@/service/Track';
|
import { useTracksWithStartName } from '@/service/Track';
|
||||||
import { useColorThemeValue } from '@/theme/ThemeContext';
|
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||||
import { Flex } from '@/ui';
|
|
||||||
|
|
||||||
export const TracksStartLetterDetailPage = () => {
|
export const TracksStartLetterDetailPage = () => {
|
||||||
const { startLetter } = useParams();
|
const { startLetter } = useParams();
|
||||||
@ -48,55 +50,50 @@ export const TracksStartLetterDetailPage = () => {
|
|||||||
<Flex
|
<Flex
|
||||||
direction="column"
|
direction="column"
|
||||||
gap="20px"
|
gap="20px"
|
||||||
style={{
|
marginX="auto"
|
||||||
margin: "0 auto",
|
padding="20px"
|
||||||
padding: "20px",
|
width="80%"
|
||||||
width: "80%",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{isLoading &&
|
{isLoading &&
|
||||||
[1, 2, 3, 4]?.map((data) => (
|
[1, 2, 3, 4]?.map((data) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
//height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data}
|
key={data}
|
||||||
style={{
|
padding="5px"
|
||||||
minWidth: "100%",
|
as="button"
|
||||||
//height:"60px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrackSkeleton />
|
<DisplayTrackSkeleton />
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
tracksStartsWith?.map((data) => (
|
tracksStartsWith?.map((data) => (
|
||||||
<Flex
|
<Box
|
||||||
|
minWidth="100%"
|
||||||
|
//height="60px"
|
||||||
|
border="1px"
|
||||||
|
borderColor="brand.900"
|
||||||
|
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||||
key={data.id}
|
key={data.id}
|
||||||
style={{
|
padding="5px"
|
||||||
minWidth: "100%",
|
as="button"
|
||||||
//height:"60px",
|
_hover={{
|
||||||
border: "1px",
|
boxShadow: 'outline-over',
|
||||||
borderColor: "brand.900",
|
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||||
backgroundColor: useColorThemeValue('#FFFFFF88', '#00000088'),
|
|
||||||
|
|
||||||
padding: "5px",
|
|
||||||
// _hover: {
|
|
||||||
// boxShadow: 'outline-over',
|
|
||||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
|
||||||
// }
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisplayTrackFull
|
<DisplayTrackFull
|
||||||
track={data}
|
track={data}
|
||||||
onClick={() => onSelectItem(data.id)}
|
onClick={() => onSelectItem(data.id)}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Box>
|
||||||
))}
|
))}
|
||||||
<EmptyEnd />
|
<EmptyEnd />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -14,7 +14,7 @@ export type ActivePlaylistServiceProps = {
|
|||||||
trackActive?: Track;
|
trackActive?: Track;
|
||||||
setNewPlaylist: (listIds: number[]) => void;
|
setNewPlaylist: (listIds: number[]) => void;
|
||||||
setNewPlaylistShuffle: (listIds: number[]) => void;
|
setNewPlaylistShuffle: (listIds: number[]) => void;
|
||||||
playInList: (id: number, listIds: number[]) => void;
|
playInList: (id: number | undefined, listIds: number[]) => void;
|
||||||
play: (id: number) => void;
|
play: (id: number) => void;
|
||||||
previous: () => void;
|
previous: () => void;
|
||||||
next: () => void;
|
next: () => void;
|
||||||
@ -65,7 +65,7 @@ export const useActivePlaylistServiceWrapped = (
|
|||||||
[setPlayTrackList, setTrackOffset]
|
[setPlayTrackList, setTrackOffset]
|
||||||
);
|
);
|
||||||
const playInList = useCallback(
|
const playInList = useCallback(
|
||||||
(id: number, listIds: number[]) => {
|
(id: number | undefined, listIds: number[]) => {
|
||||||
console.log(`Request paly in list: ${id} in ${listIds}`);
|
console.log(`Request paly in list: ${id} in ${listIds}`);
|
||||||
setPlayTrackList(listIds);
|
setPlayTrackList(listIds);
|
||||||
setTrackOffset(id);
|
setTrackOffset(id);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Album, AlbumResource } from '@/back-api';
|
import { Album, AlbumResource } from '@/back-api';
|
||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Track, TrackResource } from '@/back-api';
|
import { Track, TrackResource } from '@/back-api';
|
||||||
import { useServiceContext } from '@/service/ServiceContext';
|
import { useServiceContext } from '@/service/ServiceContext';
|
||||||
|
@ -6,13 +6,16 @@ import { useServiceContext } from '@/service/ServiceContext';
|
|||||||
import { SessionState } from '@/service/SessionState';
|
import { SessionState } from '@/service/SessionState';
|
||||||
import { isBrowser } from '@/utils/layout';
|
import { isBrowser } from '@/utils/layout';
|
||||||
import { parseToken } from '@/utils/sso';
|
import { parseToken } from '@/utils/sso';
|
||||||
|
import { createListCollection } from '@chakra-ui/react';
|
||||||
|
|
||||||
const TOKEN_KEY = 'karusic-token-key-storage';
|
const TOKEN_KEY = 'karusic-token-key-storage';
|
||||||
|
|
||||||
export const USERS_COLLECTION = [
|
export const USERS_COLLECTION = createListCollection({
|
||||||
{ label: "admin", value: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E" },
|
items: [
|
||||||
{ label: "NO_USER", value: "svelte" },
|
{ label: "admin", value: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E" },
|
||||||
];
|
{ label: "NO_USER", value: "svelte" },
|
||||||
|
],
|
||||||
|
})
|
||||||
export const USERS = {
|
export const USERS = {
|
||||||
admin:
|
admin:
|
||||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E',
|
'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxIiwiYXBwbGljYXRpb24iOiJrYXJ1c2ljIiwiaXNzIjoiS2FyQXV0aCIsInJpZ2h0Ijp7ImthcnVzaWMiOnsiQURNSU4iOnRydWUsIlVTRVIiOnRydWV9fSwibG9naW4iOiJIZWVyb1l1aSIsImV4cCI6MTcyNDIwNjc5NCwiaWF0IjoxNzI0MTY2ODM0fQ.TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E',
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ReactElement, ReactNode } from 'react';
|
import { ReactElement, ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
|
||||||
import { RenderOptions, render } from '@testing-library/react';
|
import { RenderOptions, render } from '@testing-library/react';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
|
|
||||||
import { createContext, useContext, useState, useEffect, CSSProperties, ReactNode, useCallback } from "react";
|
|
||||||
import { basicColor } from "./colors";
|
|
||||||
|
|
||||||
type themeContextProps = {
|
|
||||||
theme: string;
|
|
||||||
themeList: string[];
|
|
||||||
setTheme: (string) => void;
|
|
||||||
toggleTheme: () => void;
|
|
||||||
convertStyle: (style: CSSProperties) => CSSProperties,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const ThemeContext = createContext<themeContextProps>({
|
|
||||||
theme: "error",
|
|
||||||
themeList: ["error"],
|
|
||||||
setTheme: (_: string) => { },
|
|
||||||
toggleTheme: () => { },
|
|
||||||
convertStyle: (style: CSSProperties): CSSProperties => { return style; },
|
|
||||||
});
|
|
||||||
|
|
||||||
const themes = {
|
|
||||||
baseColors: {
|
|
||||||
...basicColor
|
|
||||||
},
|
|
||||||
themeColors: {
|
|
||||||
light: {
|
|
||||||
primary: "#007bff",
|
|
||||||
secondary: "#6c757d",
|
|
||||||
background: "#ffffff",
|
|
||||||
text: "#000000",
|
|
||||||
},
|
|
||||||
dark: {
|
|
||||||
primary: "#375a7f",
|
|
||||||
secondary: "#444444",
|
|
||||||
background: "#181818",
|
|
||||||
text: "#ffffff",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ThemeProvider({ children, themeList = ["light", "dark"] }: { children: ReactNode, themeList?: string[] }) {
|
|
||||||
const [theme, setTheme] = useState<string>(themeList[0]);
|
|
||||||
const [basicsColor, setBasicsColor] = useState<{ [key: string]: string }>({});
|
|
||||||
|
|
||||||
// update the global CSS wen theme is updated:
|
|
||||||
useEffect(() => {
|
|
||||||
// generates the colors:
|
|
||||||
const baseColor = themes.baseColors;
|
|
||||||
const newBasicsColor: { [key: string]: string } = {}
|
|
||||||
Object.entries(baseColor).forEach(([tableColorName, colorValues]) => {
|
|
||||||
Object.entries(colorValues).forEach(([key, value]) => {
|
|
||||||
const colorName = `${tableColorName}.${key}`;
|
|
||||||
newBasicsColor[colorName] = value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setBasicsColor(newBasicsColor);
|
|
||||||
|
|
||||||
const root = document.documentElement;
|
|
||||||
//const currentColorTheme = themes.baseColors[theme];
|
|
||||||
Object.entries(newBasicsColor).forEach(([key, value]) => {
|
|
||||||
const cssKeyValue = `--${key.replace(".", "-")}`;
|
|
||||||
console.log(` generate CSS color: ${cssKeyValue}:${value}`);
|
|
||||||
root.style.setProperty(cssKeyValue, value);
|
|
||||||
});
|
|
||||||
}, [theme, setBasicsColor]);
|
|
||||||
|
|
||||||
const toggleTheme = useCallback(() => {
|
|
||||||
console.log(`plop: ${theme}`);
|
|
||||||
setTheme((previous) => {
|
|
||||||
if (themeList.length <= 1) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
for (let iii = 0; iii < themeList.length - 1; iii++) {
|
|
||||||
if (themeList[iii] == theme) {
|
|
||||||
return themeList[iii + 1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return themeList[0];
|
|
||||||
})
|
|
||||||
}, [setTheme]);
|
|
||||||
|
|
||||||
const convertElementStyle = useCallback((style: CSSProperties, key: string) => {
|
|
||||||
const value = style[key];
|
|
||||||
if (typeof value !== "string") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (basicsColor[value]) {
|
|
||||||
style[key] = basicsColor[value];
|
|
||||||
}
|
|
||||||
}, [basicsColor]);
|
|
||||||
const convertStyle = useCallback((style: CSSProperties): CSSProperties => {
|
|
||||||
console.log(`plop: ${theme}`);
|
|
||||||
if (!style) {
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
const out = { ...style }
|
|
||||||
convertElementStyle(out, "background");
|
|
||||||
convertElementStyle(out, "borderColor");
|
|
||||||
convertElementStyle(out, "color");
|
|
||||||
return out;
|
|
||||||
}, [convertElementStyle]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeContext.Provider value={{ theme, themeList, setTheme, toggleTheme, convertStyle }}>
|
|
||||||
{children}
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export const useTheme = () => useContext(ThemeContext);
|
|
||||||
|
|
||||||
export function useColorThemeValue<T>(firstTheme: T, secondTheme: T) {
|
|
||||||
const { theme, themeList } = useTheme()
|
|
||||||
return theme === themeList[0] ? firstTheme : secondTheme
|
|
||||||
}
|
|
@ -1,125 +1,115 @@
|
|||||||
// Update me with other Tailwind colors or with https://smart-swatch.netlify.app/
|
|
||||||
const brand = {
|
|
||||||
50: '#e3edff',
|
|
||||||
100: '#b6c9fd',
|
|
||||||
200: '#88a5f7',
|
|
||||||
300: '#5a81f2',
|
|
||||||
400: '#2c5ded',
|
|
||||||
500: '#1543d4',
|
|
||||||
600: '#0d34a5',
|
|
||||||
700: '#062577',
|
|
||||||
800: '#02164a',
|
|
||||||
900: '#00071e',
|
|
||||||
};
|
|
||||||
|
|
||||||
const back = {
|
type PandaColorModel = {
|
||||||
50: '#f2f2f2',
|
value: string;
|
||||||
100: '#d9d9d9',
|
|
||||||
200: '#bfbfbf',
|
|
||||||
300: '#a6a6a6',
|
|
||||||
400: '#8c8c8c',
|
|
||||||
500: '#737373',
|
|
||||||
600: '#595959',
|
|
||||||
700: '#404040',
|
|
||||||
800: '#262626',
|
|
||||||
900: '#0d0d0d',
|
|
||||||
};
|
|
||||||
|
|
||||||
const green = {
|
|
||||||
50: '#f0fdf4',
|
|
||||||
100: '#dcfce7',
|
|
||||||
200: '#bbf7d0',
|
|
||||||
300: '#86efac',
|
|
||||||
400: '#4ade80',
|
|
||||||
500: '#22c55e',
|
|
||||||
600: '#16a34a',
|
|
||||||
700: '#15803d',
|
|
||||||
800: '#166534',
|
|
||||||
900: '#14532d',
|
|
||||||
};
|
|
||||||
const blue = {
|
|
||||||
50: '#eff6ff',
|
|
||||||
100: '#dbeafe',
|
|
||||||
200: '#bfdbfe',
|
|
||||||
300: '#93c5fd',
|
|
||||||
400: '#60a5fa',
|
|
||||||
500: '#3b82f6',
|
|
||||||
600: '#2563eb',
|
|
||||||
700: '#1d4ed8',
|
|
||||||
800: '#1e40af',
|
|
||||||
900: '#1e3a8a',
|
|
||||||
};
|
|
||||||
const orange = {
|
|
||||||
50: '#fff7ed',
|
|
||||||
100: '#ffedd5',
|
|
||||||
200: '#fed7aa',
|
|
||||||
300: '#fdba74',
|
|
||||||
400: '#fb923c',
|
|
||||||
500: '#f97316',
|
|
||||||
600: '#ea580c',
|
|
||||||
700: '#c2410c',
|
|
||||||
800: '#9a3412',
|
|
||||||
900: '#7c2d12',
|
|
||||||
};
|
|
||||||
const red = {
|
|
||||||
50: '#fef2f2',
|
|
||||||
100: '#fee2e2',
|
|
||||||
200: '#fecaca',
|
|
||||||
300: '#fca5a5',
|
|
||||||
400: '#f87171',
|
|
||||||
500: '#ef4444',
|
|
||||||
600: '#dc2626',
|
|
||||||
700: '#b91c1c',
|
|
||||||
800: '#991b1b',
|
|
||||||
900: '#7f1d1d',
|
|
||||||
};
|
|
||||||
const yellow = {
|
|
||||||
50: '#ffffda',
|
|
||||||
100: '#ffffad',
|
|
||||||
200: '#ffff7d',
|
|
||||||
300: '#ffff4b',
|
|
||||||
400: '#ffff1a',
|
|
||||||
500: '#e5e600',
|
|
||||||
600: '#b2b300',
|
|
||||||
700: '#7f8000',
|
|
||||||
800: '#4c4d00',
|
|
||||||
900: '#191b00',
|
|
||||||
};
|
|
||||||
const purple = {
|
|
||||||
50: '#ffe3ff',
|
|
||||||
100: '#ffb2ff',
|
|
||||||
200: '#ff80ff',
|
|
||||||
300: '#fe4efe',
|
|
||||||
400: '#fe20fe',
|
|
||||||
500: '#e50ce4',
|
|
||||||
600: '#b204b1',
|
|
||||||
700: '#80007f',
|
|
||||||
800: '#4e004d',
|
|
||||||
900: '#1d001c',
|
|
||||||
};
|
|
||||||
|
|
||||||
const cyan = {
|
|
||||||
50: '#d6ffff',
|
|
||||||
100: '#aaffff',
|
|
||||||
200: '#7affff',
|
|
||||||
300: '#47ffff',
|
|
||||||
400: '#1affff',
|
|
||||||
500: '#00e5e6',
|
|
||||||
600: '#00b2b3',
|
|
||||||
700: '#008081',
|
|
||||||
800: '#004d4e',
|
|
||||||
900: '#001b1d',
|
|
||||||
}
|
}
|
||||||
|
type ThemeModel = {
|
||||||
|
50: PandaColorModel;
|
||||||
|
100: PandaColorModel;
|
||||||
|
200: PandaColorModel;
|
||||||
|
300: PandaColorModel;
|
||||||
|
400: PandaColorModel;
|
||||||
|
500: PandaColorModel;
|
||||||
|
600: PandaColorModel;
|
||||||
|
700: PandaColorModel;
|
||||||
|
800: PandaColorModel;
|
||||||
|
900: PandaColorModel;
|
||||||
|
};
|
||||||
|
|
||||||
export const basicColor = {
|
const back: ThemeModel = {
|
||||||
green, red, orange, back, blue, yellow, purple, cyan
|
50: { value: '#ebf4fa' },
|
||||||
}
|
100: { value: '#d1dbe0' },
|
||||||
|
200: { value: '#b6c2c9' },
|
||||||
|
300: { value: '#99aab4' },
|
||||||
|
400: { value: '#7c939e' },
|
||||||
|
500: { value: '#637985' },
|
||||||
|
600: { value: '#4d5e67' },
|
||||||
|
700: { value: '#37444a' },
|
||||||
|
800: { value: '#1f292e' },
|
||||||
|
900: { value: '#020f12' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const brand: ThemeModel = {
|
||||||
|
50: { value: '#e3edff' },
|
||||||
|
100: { value: '#b6c9fd' },
|
||||||
|
200: { value: '#88a5f7' },
|
||||||
|
300: { value: '#5a81f2' },
|
||||||
|
400: { value: '#2c5ded' },
|
||||||
|
500: { value: '#1543d4' },
|
||||||
|
600: { value: '#0d34a5' },
|
||||||
|
700: { value: '#062577' },
|
||||||
|
800: { value: '#02164a' },
|
||||||
|
900: { value: '#00071e' },
|
||||||
|
};
|
||||||
|
const normalText: ThemeModel = {
|
||||||
|
50: { value: '#f2f2f2' },
|
||||||
|
100: { value: '#d9d9d9' },
|
||||||
|
200: { value: '#bfbfbf' },
|
||||||
|
300: { value: '#a6a6a6' },
|
||||||
|
400: { value: '#8c8c8c' },
|
||||||
|
500: { value: '#737373' },
|
||||||
|
600: { value: '#595959' },
|
||||||
|
700: { value: '#404040' },
|
||||||
|
800: { value: '#262626' },
|
||||||
|
900: { value: '#0d0d0d' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const green: ThemeModel = {
|
||||||
|
50: { value: '#f0fdf4' },
|
||||||
|
100: { value: '#dcfce7' },
|
||||||
|
200: { value: '#bbf7d0' },
|
||||||
|
300: { value: '#86efac' },
|
||||||
|
400: { value: '#4ade80' },
|
||||||
|
500: { value: '#22c55e' },
|
||||||
|
600: { value: '#16a34a' },
|
||||||
|
700: { value: '#15803d' },
|
||||||
|
800: { value: '#166534' },
|
||||||
|
900: { value: '#14532d' },
|
||||||
|
};
|
||||||
|
const blue: ThemeModel = {
|
||||||
|
50: { value: '#eff6ff' },
|
||||||
|
100: { value: '#dbeafe' },
|
||||||
|
200: { value: '#bfdbfe' },
|
||||||
|
300: { value: '#93c5fd' },
|
||||||
|
400: { value: '#60a5fa' },
|
||||||
|
500: { value: '#3b82f6' },
|
||||||
|
600: { value: '#2563eb' },
|
||||||
|
700: { value: '#1d4ed8' },
|
||||||
|
800: { value: '#1e40af' },
|
||||||
|
900: { value: '#1e3a8a' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const orange: ThemeModel = {
|
||||||
|
50: { value: '#fff7ed' },
|
||||||
|
100: { value: '#ffedd5' },
|
||||||
|
200: { value: '#fed7aa' },
|
||||||
|
300: { value: '#fdba74' },
|
||||||
|
400: { value: '#fb923c' },
|
||||||
|
500: { value: '#f97316' },
|
||||||
|
600: { value: '#ea580c' },
|
||||||
|
700: { value: '#c2410c' },
|
||||||
|
800: { value: '#9a3412' },
|
||||||
|
900: { value: '#7c2d12' },
|
||||||
|
};
|
||||||
|
const red: ThemeModel = {
|
||||||
|
50: { value: '#fef2f2' },
|
||||||
|
100: { value: '#fee2e2' },
|
||||||
|
200: { value: '#fecaca' },
|
||||||
|
300: { value: '#fca5a5' },
|
||||||
|
400: { value: '#f87171' },
|
||||||
|
500: { value: '#ef4444' },
|
||||||
|
600: { value: '#dc2626' },
|
||||||
|
700: { value: '#b91c1c' },
|
||||||
|
800: { value: '#991b1b' },
|
||||||
|
900: { value: '#7f1d1d' },
|
||||||
|
};
|
||||||
|
|
||||||
export const colors = {
|
export const colors = {
|
||||||
|
// Update me with other Tailwind colors or with https://smart-swatch.netlify.app/
|
||||||
brand: brand,
|
brand: brand,
|
||||||
back: back,
|
back: back,
|
||||||
|
text: normalText,
|
||||||
success: green,
|
success: green,
|
||||||
error: red,
|
error: red,
|
||||||
warning: orange,
|
warning: orange,
|
||||||
|
danger: red,
|
||||||
} as const;
|
} as const;
|
||||||
|
10888
front/src/theme/config sample.ts
Normal file
10888
front/src/theme/config sample.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
|||||||
export default {
|
|
||||||
sizes: {
|
|
||||||
'2xs': {
|
|
||||||
fontSize: '0.5em',
|
|
||||||
},
|
|
||||||
xs: {
|
|
||||||
fontSize: '0.6em',
|
|
||||||
},
|
|
||||||
sm: {
|
|
||||||
fontSize: '0.7em',
|
|
||||||
},
|
|
||||||
md: {
|
|
||||||
fontSize: '0.8em',
|
|
||||||
textTransform: 'none',
|
|
||||||
},
|
|
||||||
lg: {
|
|
||||||
fontSize: '0.9em',
|
|
||||||
textTransform: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultProps: {
|
|
||||||
size: 'md',
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,103 +0,0 @@
|
|||||||
|
|
||||||
// https://medium.com/@a.heydari.dev/simplifying-chakra-ui-v3-recipes-vs-chakra-factory-a-developers-perspective-4020b62f1b4d
|
|
||||||
|
|
||||||
// const shimmer = keyframes`
|
|
||||||
// 100% {
|
|
||||||
// transform: translateX(100%);
|
|
||||||
// }
|
|
||||||
// `;
|
|
||||||
|
|
||||||
export const customVariant = ({ bg, bgHover, bgActive, color, colorHover, boxShadowHover }) => {
|
|
||||||
return {
|
|
||||||
bg,
|
|
||||||
color,
|
|
||||||
border: '1px solid transparent',
|
|
||||||
_focus: {
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: 'black',
|
|
||||||
},
|
|
||||||
_hover: {
|
|
||||||
bg: bgHover,
|
|
||||||
color: colorHover,
|
|
||||||
boxShadow: boxShadowHover,
|
|
||||||
_disabled: {
|
|
||||||
bg,
|
|
||||||
boxShadow: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_active: {
|
|
||||||
bg: bgActive,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonTheme = {
|
|
||||||
primary:
|
|
||||||
customVariant({
|
|
||||||
bg: { _light: 'brand.600', _dark: 'brand.300' },
|
|
||||||
bgHover: { _light: 'brand.700', _dark: 'brand.400' },
|
|
||||||
bgActive: { _light: 'brand.600', _dark: 'brand.300' },
|
|
||||||
color: { _light: 'white', _dark: 'brand.900' },
|
|
||||||
colorHover: { _light: 'brand.800', _dark: 'brand.100' },
|
|
||||||
boxShadowHover: 'outline-over'
|
|
||||||
}),
|
|
||||||
secondary:
|
|
||||||
customVariant({
|
|
||||||
bg: { _light: 'brand.100', _dark: 'brand.900' },
|
|
||||||
bgHover: { _light: 'brand.200', _dark: 'brand.800' },
|
|
||||||
bgActive: { _light: 'brand.300', _dark: 'brand.700' },
|
|
||||||
color: { _light: 'brand.700', _dark: 'brand.50' },
|
|
||||||
colorHover: { _light: 'brand.800', _dark: 'brand.100' },
|
|
||||||
boxShadowHover: 'outline-over',
|
|
||||||
}),
|
|
||||||
danger:
|
|
||||||
customVariant({
|
|
||||||
bg: { _light: 'error.600', _dark: 'error.600' },
|
|
||||||
bgHover: { _light: 'error.700', _dark: 'error.500' },
|
|
||||||
bgActive: { _light: 'error.600', _dark: 'error.500' },
|
|
||||||
color: { _light: 'white', _dark: 'error.900' },
|
|
||||||
colorHover: { _light: 'error.700', _dark: 'error.900' },
|
|
||||||
boxShadowHover: 'outline-over',
|
|
||||||
}),
|
|
||||||
success:
|
|
||||||
customVariant({
|
|
||||||
bg: { _light: 'green.300', _dark: 'green.300' },
|
|
||||||
bgHover: { _light: 'green.400', _dark: 'green.400' },
|
|
||||||
bgActive: { _light: 'green.500', _dark: 'green.400' },
|
|
||||||
color: { _light: 'white', _dark: 'green.900' },
|
|
||||||
colorHover: { _light: 'green.500', _dark: 'green.900' },
|
|
||||||
boxShadowHover: 'outline-over',
|
|
||||||
}),
|
|
||||||
progress:
|
|
||||||
{
|
|
||||||
bg: { _light: `brand.500`, _dark: `brand.300` },
|
|
||||||
overflow: 'hidden',
|
|
||||||
/*
|
|
||||||
_after: !props.isLoading
|
|
||||||
? {
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
transform: 'translateX(-100%)',
|
|
||||||
bgGradient: `linear(90deg, brand.100/0 0%, brand.100/2 20%, brand.100/5 60%, v.100/0`,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
|
|
||||||
menu: {
|
|
||||||
bg: 'back.100',
|
|
||||||
color: 'brand.900',
|
|
||||||
borderRadius: 0,
|
|
||||||
border: 0,
|
|
||||||
_hover: { background: 'back.300' },
|
|
||||||
_focus: { border: 'none' },
|
|
||||||
fontSize: '20px',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default buttonTheme;
|
|
@ -1,5 +0,0 @@
|
|||||||
export default {
|
|
||||||
defaultProps: {
|
|
||||||
colorScheme: 'brand',
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
const drawerRecipe = {
|
|
||||||
base: {
|
|
||||||
bg: { _light: 'white', _dark: 'gray.800' },
|
|
||||||
color: { _light: 'gray.900', _dark: 'whiteAlpha.900' },
|
|
||||||
boxShadow: { _light: 'lg', _dark: 'dark-lg' },
|
|
||||||
padding: 4,
|
|
||||||
borderRadius: 'md',
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
solid: {
|
|
||||||
bg: { _light: 'blue.500', _dark: 'blue.300' },
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
border: '1px solid',
|
|
||||||
borderColor: { _light: 'gray.300', _dark: 'whiteAlpha.300' },
|
|
||||||
bg: { _light: 'white', _dark: 'gray.900' },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: 'solid',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default drawerRecipe;
|
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const flexTheme = {
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
'@menu': {
|
|
||||||
bg: { _light: 'back.100', _dark: 'back.800' },
|
|
||||||
color: { _light: 'brand.900', _dark: 'brand.100' },
|
|
||||||
borderRadius: 0,
|
|
||||||
border: 0,
|
|
||||||
_hover: { background: { _light: 'back.300', _dark: 'back.600' } },
|
|
||||||
_focus: { border: 'none' },
|
|
||||||
fontSize: '20px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default flexTheme;
|
|
@ -1,13 +0,0 @@
|
|||||||
export { default as Badge } from './badge';
|
|
||||||
export { default as Button } from './button';
|
|
||||||
export { default as Checkbox } from './checkbox';
|
|
||||||
export { default as Input } from './input';
|
|
||||||
//export { default as NumberInput } from './numberInput.ts_';
|
|
||||||
export { default as Popover } from './popover';
|
|
||||||
export { default as Radio } from './radio';
|
|
||||||
export { default as Select } from './select';
|
|
||||||
export { default as Switch } from './switch';
|
|
||||||
export { default as Textarea } from './textarea';
|
|
||||||
//export { default as Modal } from './modal';
|
|
||||||
export { default as Flex } from './flex';
|
|
||||||
export { default as Drawer } from './drawer';
|
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
const inputTheme = {
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
outline: {
|
|
||||||
field: {
|
|
||||||
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
|
|
||||||
borderColor: { _light: 'gray.200', _dark: 'whiteAlpha.100' },
|
|
||||||
color: { _light: 'gray.800', _dark: 'gray.50' },
|
|
||||||
_focus: {
|
|
||||||
borderColor: { _light: 'gray.800', _dark: 'gray.50' },
|
|
||||||
boxShadow: `0 0 0 1px gray.800`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default inputTheme;
|
|
@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
const { definePartsStyle, defineMultiStyleConfig } =
|
|
||||||
createMultiStyleConfigHelpers(parts.keys);
|
|
||||||
|
|
||||||
const baseStyle = definePartsStyle({
|
|
||||||
header: {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const modalTheme = defineMultiStyleConfig({
|
|
||||||
baseStyle,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default modalTheme;
|
|
@ -1,77 +0,0 @@
|
|||||||
export default {
|
|
||||||
sizes: {
|
|
||||||
'3xs': {
|
|
||||||
content: {
|
|
||||||
width: '3xs',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'2xs': {
|
|
||||||
content: {
|
|
||||||
width: '2xs',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xs: {
|
|
||||||
content: {
|
|
||||||
width: 'xs',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sm: {
|
|
||||||
content: {
|
|
||||||
width: 'sm',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
md: {
|
|
||||||
content: {
|
|
||||||
width: 'md',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lg: {
|
|
||||||
content: {
|
|
||||||
width: 'lg',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xl: {
|
|
||||||
content: {
|
|
||||||
width: 'xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'2xl': {
|
|
||||||
content: {
|
|
||||||
width: '2xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'3xl': {
|
|
||||||
content: {
|
|
||||||
width: '3xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'4xl': {
|
|
||||||
content: {
|
|
||||||
width: '4xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'5xl': {
|
|
||||||
content: {
|
|
||||||
width: '5xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'6xl': {
|
|
||||||
content: {
|
|
||||||
width: '6xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'7xl': {
|
|
||||||
content: {
|
|
||||||
width: '7xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'8xl': {
|
|
||||||
content: {
|
|
||||||
width: '8xl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultProps: {
|
|
||||||
size: 'xs',
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
export default {
|
|
||||||
defaultProps: {
|
|
||||||
colorScheme: 'brand',
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
const selectTheme = {
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
outline: {
|
|
||||||
field: {
|
|
||||||
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
|
|
||||||
borderColor: { _light: 'blackAlpha.100', _dark: 'whiteAlpha.100' },
|
|
||||||
_focus: {
|
|
||||||
borderColor: { _light: 'brand.500', _dark: 'brand.300' },
|
|
||||||
boxShadow: `0 0 0 1px black`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default selectTheme;
|
|
@ -1,5 +0,0 @@
|
|||||||
export default {
|
|
||||||
defaultProps: {
|
|
||||||
colorScheme: 'brand',
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
const textAreaTheme = {
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
outline: {
|
|
||||||
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
|
|
||||||
borderColor: { _light: 'blackAlpha.100', _dark: 'whiteAlpha.100' },
|
|
||||||
_focus: {
|
|
||||||
borderColor: { _light: 'brand.500', _dark: 'brand.300' },
|
|
||||||
boxShadow: `0 0 0 1px brand.500`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
export default textAreaTheme;
|
|
76
front/src/theme/theme.ts
Normal file
76
front/src/theme/theme.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { createSystem, defaultConfig, mergeConfigs, SystemConfig } from "@chakra-ui/react"
|
||||||
|
import { colors } from "./colors"
|
||||||
|
|
||||||
|
const baseTheme: SystemConfig = {
|
||||||
|
globalCss: {
|
||||||
|
body: {
|
||||||
|
overflowY: 'none',
|
||||||
|
bg: { _light: 'back.50', _dark: 'back.700' },
|
||||||
|
color: { _light: 'text.900', _dark: 'text.50' },
|
||||||
|
fontFamily: 'Roboto, Helvetica, Arial, "sans-serif"',
|
||||||
|
},
|
||||||
|
svg: {
|
||||||
|
width: "32px",
|
||||||
|
height: "32px",
|
||||||
|
aspectRatio: "square",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
slotRecipes: {
|
||||||
|
dialog: {
|
||||||
|
slots: [
|
||||||
|
"header"
|
||||||
|
],
|
||||||
|
base: {
|
||||||
|
header: {
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: "2xl",
|
||||||
|
color: { _dark: "brand.400", _light: "brand.500" }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
recipes: {
|
||||||
|
button: {
|
||||||
|
base: {
|
||||||
|
borderRadius: 0,
|
||||||
|
_hover: {
|
||||||
|
//boxShadow: "3px 5px 8px gray"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tokens: {
|
||||||
|
fonts: {
|
||||||
|
heading: { value: `Roboto, Helvetica, Arial, "sans-serif"` },
|
||||||
|
body: { value: `Roboto, Helvetica, Arial, "sans-serif"` },
|
||||||
|
},
|
||||||
|
colors,
|
||||||
|
},
|
||||||
|
semanticTokens: {
|
||||||
|
colors: {
|
||||||
|
"@danger": {
|
||||||
|
solid: { value: "{colors.danger.500}" },
|
||||||
|
contrast: { value: "{colors.danger.100}" },
|
||||||
|
fg: { value: "{colors.danger.900}" },
|
||||||
|
muted: { value: "{colors.danger.100}" },
|
||||||
|
subtle: { value: "{colors.danger.200}" },
|
||||||
|
emphasized: { value: "{colors.danger.300}" },
|
||||||
|
focusRing: { value: "{colors.danger.500}" },
|
||||||
|
},
|
||||||
|
"brand": {
|
||||||
|
solid: { value: "{colors.brand.500}" },
|
||||||
|
contrast: { value: "{colors.brand.100}" },
|
||||||
|
fg: { value: "{colors.brand.900}" },
|
||||||
|
muted: { value: "{colors.brand.100}" },
|
||||||
|
subtle: { value: "{colors.brand.200}" },
|
||||||
|
emphasized: { value: "{colors.brand.300}" },
|
||||||
|
focusRing: { value: "{colors.brand.500}" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const config = mergeConfigs(defaultConfig, baseTheme);
|
||||||
|
//console.log("defaultConfig: " + JSON.stringify(defaultConfig, null, 2));
|
||||||
|
export const systemTheme = createSystem(config);
|
5
front/src/types/theme.d.ts
vendored
Normal file
5
front/src/types/theme.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { ThemeTypings } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { colors } from '@/theme/colors';
|
||||||
|
|
||||||
|
export type ColorSchemes = ThemeTypings['colorSchemes'] | keyof typeof colors;
|
@ -1,30 +0,0 @@
|
|||||||
import { useTheme } from "@/theme/ThemeContext";
|
|
||||||
import { ReactNode, CSSProperties } from "react";
|
|
||||||
|
|
||||||
export type ButtonProps = {
|
|
||||||
children: ReactNode;
|
|
||||||
onClick?: () => void;
|
|
||||||
style?: CSSProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Button = ({ children, onClick, style }: ButtonProps) => {
|
|
||||||
const { convertStyle } = useTheme();
|
|
||||||
const themedStyle = style ? convertStyle({
|
|
||||||
padding: '10px 20px',
|
|
||||||
backgroundColor: '#3182CE',
|
|
||||||
color: '#FFF',
|
|
||||||
border: 'none',
|
|
||||||
borderRadius: '4px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
fontSize: '16px',
|
|
||||||
...style,
|
|
||||||
}) : undefined;
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={onClick}
|
|
||||||
style={themedStyle}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,21 +0,0 @@
|
|||||||
import { useTheme } from '@/theme/ThemeContext';
|
|
||||||
import { CSSProperties, ReactNode } from 'react';
|
|
||||||
|
|
||||||
export type DivProps = {
|
|
||||||
children?: ReactNode;
|
|
||||||
onClick?: () => void;
|
|
||||||
style?: CSSProperties
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Div = ({ children, onClick, style }: DivProps) => {
|
|
||||||
const { convertStyle } = useTheme();
|
|
||||||
const themedStyle = style ? convertStyle(style) : undefined;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onClick={onClick}
|
|
||||||
style={themedStyle}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user