diff --git a/front2/src/assets/images/404.svg b/front2/src/assets/images/404.svg
new file mode 100644
index 0000000..c0cff3f
--- /dev/null
+++ b/front2/src/assets/images/404.svg
@@ -0,0 +1,72 @@
+
+
diff --git a/front2/src/back-api/model/track.ts b/front2/src/back-api/model/track.ts
index c2b0fb8..7503536 100644
--- a/front2/src/back-api/model/track.ts
+++ b/front2/src/back-api/model/track.ts
@@ -1,61 +1,62 @@
/**
* Interface of the server (auto-generated code)
*/
-import { z as zod } from "zod";
+import { z as zod } from 'zod';
-import {ZodUUID} from "./uuid";
-import {ZodLong} from "./long";
-import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
+import {
+ ZodGenericDataSoftDelete,
+ ZodGenericDataSoftDeleteWrite,
+} from './generic-data-soft-delete';
+import { ZodLong } from './long';
+import { ZodUUID } from './uuid';
export const ZodTrack = ZodGenericDataSoftDelete.extend({
- name: zod.string().max(256).optional(),
- description: zod.string().optional(),
- /**
- * List of Id of the specific covers
- */
- covers: zod.array(ZodUUID).optional(),
- genderId: ZodLong.optional(),
- albumId: ZodLong.optional(),
- track: ZodLong.optional(),
- dataId: ZodUUID.optional(),
- artists: zod.array(ZodLong),
-
+ name: zod.string().max(256).optional(),
+ description: zod.string().optional(),
+ /**
+ * List of Id of the specific covers
+ */
+ covers: zod.array(ZodUUID).optional(),
+ genderId: ZodLong.optional(),
+ albumId: ZodLong.optional(),
+ track: ZodLong.optional(),
+ dataId: ZodUUID.optional(),
+ artists: zod.array(ZodLong),
});
export type Track = zod.infer;
export function isTrack(data: any): data is Track {
- try {
- ZodTrack.parse(data);
- return true;
- } catch (e: any) {
- console.log(`Fail to parse data type='ZodTrack' error=${e}`);
- return false;
- }
+ try {
+ ZodTrack.parse(data);
+ return true;
+ } catch (e: any) {
+ console.log(`Fail to parse data type='ZodTrack' error=${e}`);
+ return false;
+ }
}
export const ZodTrackWrite = ZodGenericDataSoftDeleteWrite.extend({
- name: zod.string().max(256).nullable().optional(),
- description: zod.string().nullable().optional(),
- /**
- * List of Id of the specific covers
- */
- covers: zod.array(ZodUUID).nullable().optional(),
- genderId: ZodLong.nullable().optional(),
- albumId: ZodLong.nullable().optional(),
- track: ZodLong.nullable().optional(),
- dataId: ZodUUID.nullable().optional(),
- artists: zod.array(ZodLong).optional(),
-
+ name: zod.string().max(256).nullable().optional(),
+ description: zod.string().nullable().optional(),
+ /**
+ * List of Id of the specific covers
+ */
+ covers: zod.array(ZodUUID).nullable().optional(),
+ genderId: ZodLong.nullable().optional(),
+ albumId: ZodLong.nullable().optional(),
+ track: ZodLong.nullable().optional(),
+ dataId: ZodUUID.nullable().optional(),
+ artists: zod.array(ZodLong).optional(),
});
export type TrackWrite = zod.infer;
export function isTrackWrite(data: any): data is TrackWrite {
- try {
- ZodTrackWrite.parse(data);
- return true;
- } catch (e: any) {
- console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
- return false;
- }
+ try {
+ ZodTrackWrite.parse(data);
+ return true;
+ } catch (e: any) {
+ console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
+ return false;
+ }
}
diff --git a/front2/src/components/AudioPlayer.tsx b/front2/src/components/AudioPlayer.tsx
index 3bef04a..dafec50 100644
--- a/front2/src/components/AudioPlayer.tsx
+++ b/front2/src/components/AudioPlayer.tsx
@@ -30,20 +30,20 @@ import {
} from 'react-icons/md';
import { useActivePlaylistService } from '@/service/ActivePlaylist';
+import { useSpecificAlbum } from '@/service/Album';
+import { useSpecificArtists } from '@/service/Artist';
+import { useSpecificGender } from '@/service/Gender';
import { useSpecificTrack } from '@/service/Track';
import { DataUrlAccess } from '@/utils/data-url-access';
import { useThemeMode } from '@/utils/theme-tools';
import { isNullOrUndefined } from '@/utils/validator';
-
-
-
export enum PlayMode {
PLAY_ONE,
PLAY_ALL,
PLAY_ONE_LOOP,
PLAY_ALL_LOOP,
-};
+}
const playModeIcon = {
[PlayMode.PLAY_ONE]: ,
@@ -65,7 +65,7 @@ const formatTime = (time) => {
return '00:00';
};
-export const AudioPlayer = ({ }: AudioPlayerProps) => {
+export const AudioPlayer = ({}: AudioPlayerProps) => {
const { mode } = useThemeMode();
const { playTrackList, trackOffset, previous, next, first } =
useActivePlaylistService();
@@ -77,6 +77,10 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
const { dataTrack } = useSpecificTrack(
trackOffset !== undefined ? playTrackList[trackOffset] : undefined
);
+ const { dataAlbum } = useSpecificAlbum(dataTrack?.albumId);
+ const { dataGender } = useSpecificGender(dataTrack?.genderId);
+ const { dataArtists } = useSpecificArtists(dataTrack?.artists);
+
const [mediaSource, setMediaSource] = useState('');
useEffect(() => {
setMediaSource(
@@ -180,7 +184,7 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
} else {
return PlayMode.PLAY_ONE;
}
- })
+ });
};
/**
* Call when meta-data is updated
@@ -198,7 +202,7 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
console.log(`onTimeUpdate ${audioRef.current.currentTime}`);
setTimeProgress(audioRef.current.currentTime);
};
- const onDurationChange = (event) => { };
+ const onDurationChange = (event) => {};
const onChangeStateToPlay = () => {
setIsPlaying(true);
};
@@ -207,124 +211,134 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
};
return (
<>
-
-
- {dataTrack?.name ?? '???'}
-
-
- artist / title album
-
-
-
-
-
-
-
-
-
-
-
-
+ {dataTrack?.name ?? '???'}
+
- {formatTime(timeProgress)}
-
-
- {formatTime(duration)}
+ {dataArtists.map((data) => data.name).join(', ')} /{' '}
+ {dataAlbum && dataAlbum?.name}
+ {dataGender && ` / ${dataGender.name}`}
+
+
+
+
+
+
+
+
+
+
+
+
+ {formatTime(timeProgress)}
+
+
+ {formatTime(duration)}
+
+
+
+
+ ) : (
+
+ )
+ }
+ onClick={onPlay}
+ />
+ }
+ onClick={onStop}
+ />
+ }
+ onClick={onNavigatePrevious}
+ marginLeft="auto"
+ />
+ }
+ onClick={onFastRewind}
+ />
+ }
+ onClick={onFastForward}
+ />
+ }
+ marginRight="auto"
+ onClick={onNavigateNext}
+ />
+
+
-
- :
- }
- onClick={onPlay}
- />
- }
- onClick={onStop}
- />
- }
- onClick={onNavigatePrevious}
- marginLeft="auto"
- />
- }
- onClick={onFastRewind}
- />
- }
- onClick={onFastForward}
- />
- }
- marginRight="auto"
- onClick={onNavigateNext}
- />
-
-
-
+ )}
;
+ return ;
};
diff --git a/front2/src/components/SearchInput.tsx b/front2/src/components/SearchInput.tsx
new file mode 100644
index 0000000..7c129dc
--- /dev/null
+++ b/front2/src/components/SearchInput.tsx
@@ -0,0 +1,67 @@
+import { useState } from 'react';
+import React from 'react';
+
+import {
+ Input,
+ InputGroup,
+ InputLeftElement,
+ useOutsideClick,
+} from '@chakra-ui/react';
+import { MdSearch } from 'react-icons/md';
+
+export type SearchInputProps = {
+ onChange?: (data?: string) => void;
+ onSubmit?: (data?: string) => void;
+};
+
+export const SearchInput = ({
+ onChange: onChangeValue,
+ onSubmit: onSubmitValue,
+}: SearchInputProps) => {
+ const [inputData, setInputData] = useState(undefined);
+ const [searchInputProperty, setSearchInputProperty] =
+ useState(undefined);
+ function onFocusKeep(): void {
+ setSearchInputProperty({
+ width: '70%',
+ maxWidth: '70%',
+ });
+ }
+ function onFocusLost(): void {
+ setSearchInputProperty({
+ width: '250px',
+ });
+ }
+ const ref = React.useRef(null);
+ // TODO: find a better way...
+ useOutsideClick({
+ ref: ref,
+ handler: onFocusLost,
+ });
+ function onChange(event): void {
+ const data =
+ event.target.value.length === 0 ? undefined : event.target.value;
+ setInputData(data);
+ if (onChangeValue) {
+ onChangeValue(data);
+ }
+ }
+ function onSubmit(): void {
+ if (onSubmitValue) {
+ onSubmitValue(inputData);
+ }
+ }
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/front2/src/components/TopBar/TopBar.tsx b/front2/src/components/TopBar/TopBar.tsx
index ec6f6d1..b3f187a 100644
--- a/front2/src/components/TopBar/TopBar.tsx
+++ b/front2/src/components/TopBar/TopBar.tsx
@@ -20,7 +20,6 @@ import {
import {
LuAlignJustify,
LuArrowBigLeft,
- LuArrowRightSquare,
LuArrowUpSquare,
LuHelpCircle,
LuHome,
@@ -44,9 +43,10 @@ export const TOP_BAR_HEIGHT = '50px';
export type TopBarProps = {
children?: ReactNode;
+ title?: string;
};
-export const TopBar = ({ children }: TopBarProps) => {
+export const TopBar = ({ title, children }: TopBarProps) => {
const { mode, colorMode, toggleColorMode } = useThemeMode();
const buttonProperty = {
variant: '@menu',
@@ -86,69 +86,73 @@ export const TopBar = ({ children }: TopBarProps) => {
boxShadow={'0px 2px 4px ' + colors.back[900]}
zIndex={200}
>
-