[FEAT] add the stack needed
This commit is contained in:
parent
86fec254aa
commit
bd66703a08
@ -28,35 +28,33 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Initialization extends MigrationSqlStep {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Initialization.class);
|
||||
|
||||
|
||||
public static final int KARSO_INITIALISATION_ID = 1;
|
||||
|
||||
public static final List<Class<?>> CLASSES_BASE = List.of(Data.class, Media.class, Type.class, Series.class,
|
||||
Season.class, User.class, UserMediaAdvancement.class);
|
||||
|
||||
|
||||
public static final List<Class<?>> CLASSES_BASE = List.of(Data.class, Media.class, Type.class, Series.class, Season.class, User.class, UserMediaAdvancement.class);
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Initialization";
|
||||
}
|
||||
|
||||
|
||||
public static void generateObjects() throws Exception {
|
||||
LOGGER.info("Generate APIs");
|
||||
final List<Class<?>> listOfResources = List.of(Front.class, HealthCheck.class, SeasonResource.class,
|
||||
SeriesResource.class, TypeResource.class, UserMediaAdvancementResource.class, UserResource.class,
|
||||
MediaResource.class, DataResource.class);
|
||||
final List<Class<?>> listOfResources = List.of(Front.class, HealthCheck.class, SeasonResource.class, SeriesResource.class, TypeResource.class, UserMediaAdvancementResource.class,
|
||||
UserResource.class, MediaResource.class, DataResource.class);
|
||||
final AnalyzeApi api = new AnalyzeApi();
|
||||
api.addAllApi(listOfResources);
|
||||
api.addModel(JwtToken.class);
|
||||
TsGenerateApi.generateApi(api, "../front/src/back-api/");
|
||||
LOGGER.info("Generate APIs (DONE)");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void generateStep() throws Exception {
|
||||
for (final Class<?> clazz : CLASSES_BASE) {
|
||||
addClass(clazz);
|
||||
}
|
||||
|
||||
|
||||
addAction((final DBAccess da) -> {
|
||||
final List<Type> data = List.of(//
|
||||
new Type("Documentary", "Documentary (animals, space, earth...)"), //
|
||||
@ -89,7 +87,7 @@ public class Initialization extends MigrationSqlStep {
|
||||
ALTER TABLE `userMediaAdvancement` AUTO_INCREMENT = 1000;
|
||||
""", "mysql");
|
||||
}
|
||||
|
||||
|
||||
public static void dropAll(final DBAccess da) {
|
||||
for (final Class<?> element : CLASSES_BASE) {
|
||||
try {
|
||||
@ -100,7 +98,7 @@ public class Initialization extends MigrationSqlStep {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void cleanAll(final DBAccess da) {
|
||||
for (final Class<?> element : CLASSES_BASE) {
|
||||
try {
|
||||
|
@ -91,8 +91,11 @@ public class Migration20250214 extends MigrationSqlStep {
|
||||
}
|
||||
}
|
||||
});
|
||||
// addAction("""
|
||||
// ALTER TABLE `media` DROP `dataId`;
|
||||
// """);
|
||||
addAction("""
|
||||
ALTER TABLE `media` DROP `dataId`;
|
||||
ALTER TABLE `media` CHANGE `dataId` `dataIdOld` binary(16);
|
||||
""");
|
||||
addAction("""
|
||||
ALTER TABLE `media` CHANGE `dataOid` `dataId` binary(12) NOT NULL;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.kar.karideo.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.types.ObjectId;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
@ -31,6 +32,9 @@ public class Media extends GenericDataSoftDelete {
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Data.class)
|
||||
@Column(nullable = false)
|
||||
public ObjectId dataId;
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Data.class)
|
||||
@Column(nullable = false)
|
||||
public UUID dataIdOld;
|
||||
@Schema(description = "Type of the media")
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Type.class)
|
||||
public Long typeId;
|
||||
|
@ -29,63 +29,64 @@
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@locator/babel-jsx": "0.4.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@chakra-ui/cli": "3.7.0",
|
||||
"@chakra-ui/react": "3.7.0",
|
||||
"@chakra-ui/cli": "3.8.1",
|
||||
"@chakra-ui/react": "3.8.1",
|
||||
"@emotion/react": "11.14.0",
|
||||
"allotment": "1.20.2",
|
||||
"allotment": "1.20.3",
|
||||
"css-mediaquery": "0.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
"history": "5.3.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "19.0.0-rc.1",
|
||||
"react-dom": "19.0.0-rc.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-error-boundary": "5.0.0",
|
||||
"react-icons": "5.4.0",
|
||||
"react-router-dom": "7.1.5",
|
||||
"react-icons": "5.5.0",
|
||||
"react-router-dom": "7.2.0",
|
||||
"react-select": "5.10.0",
|
||||
"react-use": "17.6.0",
|
||||
"zod": "3.24.1",
|
||||
"zod": "3.24.2",
|
||||
"zustand": "5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/styled-system": "^2.12.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@storybook/addon-actions": "8.5.4",
|
||||
"@storybook/addon-essentials": "8.5.4",
|
||||
"@storybook/addon-links": "8.5.4",
|
||||
"@storybook/addon-mdx-gfm": "8.5.4",
|
||||
"@storybook/react": "8.5.4",
|
||||
"@storybook/react-vite": "8.5.4",
|
||||
"@storybook/theming": "8.5.4",
|
||||
"@storybook/addon-actions": "8.5.8",
|
||||
"@storybook/addon-essentials": "8.5.8",
|
||||
"@storybook/addon-links": "8.5.8",
|
||||
"@storybook/addon-mdx-gfm": "8.5.8",
|
||||
"@storybook/react": "8.5.8",
|
||||
"@storybook/react-vite": "8.5.8",
|
||||
"@storybook/theming": "8.5.8",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.2.0",
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "5.2.2",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "22.13.1",
|
||||
"@types/react": "19.0.8",
|
||||
"@types/react-dom": "19.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.1",
|
||||
"@typescript-eslint/parser": "8.24.1",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-react": "7.37.4",
|
||||
"eslint-plugin-react-hooks": "5.1.0",
|
||||
"eslint-plugin-storybook": "0.11.2",
|
||||
"eslint-plugin-storybook": "0.11.3",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"knip": "5.44.0",
|
||||
"knip": "5.44.4",
|
||||
"lint-staged": "15.4.3",
|
||||
"npm-check-updates": "^17.1.14",
|
||||
"prettier": "3.5.0",
|
||||
"puppeteer": "24.2.0",
|
||||
"prettier": "3.5.1",
|
||||
"puppeteer": "24.2.1",
|
||||
"react-is": "19.0.0",
|
||||
"storybook": "8.5.4",
|
||||
"storybook": "8.5.8",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"vite": "6.1.0",
|
||||
"vitest": "3.0.5"
|
||||
"vite": "6.1.1",
|
||||
"vitest": "3.0.6"
|
||||
}
|
||||
}
|
||||
|
2521
front/pnpm-lock.yaml
generated
2521
front/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
62
front/src/app/back-api/model/data.ts
Normal file
62
front/src/app/back-api/model/data.ts
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodUUIDGenericDataSoftDelete, ZodUUIDGenericDataSoftDeleteWrite } from "./uuid-generic-data-soft-delete";
|
||||
|
||||
export const ZodData = ZodUUIDGenericDataSoftDelete.extend({
|
||||
/**
|
||||
* Sha512 of the data
|
||||
*/
|
||||
sha512: zod.string().max(128),
|
||||
/**
|
||||
* Mime -type of the media
|
||||
*/
|
||||
mimeType: zod.string().max(128),
|
||||
/**
|
||||
* Size in Byte of the data
|
||||
*/
|
||||
size: ZodLong,
|
||||
|
||||
});
|
||||
|
||||
export type Data = zod.infer<typeof ZodData>;
|
||||
|
||||
export function isData(data: any): data is Data {
|
||||
try {
|
||||
ZodData.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodData' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodDataWrite = ZodUUIDGenericDataSoftDeleteWrite.extend({
|
||||
/**
|
||||
* Sha512 of the data
|
||||
*/
|
||||
sha512: zod.string().max(128).optional(),
|
||||
/**
|
||||
* Mime -type of the media
|
||||
*/
|
||||
mimeType: zod.string().max(128).optional(),
|
||||
/**
|
||||
* Size in Byte of the data
|
||||
*/
|
||||
size: ZodLong.optional(),
|
||||
|
||||
});
|
||||
|
||||
export type DataWrite = zod.infer<typeof ZodDataWrite>;
|
||||
|
||||
export function isDataWrite(data: any): data is DataWrite {
|
||||
try {
|
||||
ZodDataWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodDataWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUIDGenericData, ZodUUIDGenericDataWrite } from "./uuid-generic-data";
|
||||
|
||||
export const ZodUUIDGenericDataSoftDelete = ZodUUIDGenericData.extend({
|
||||
/**
|
||||
* Deleted state
|
||||
*/
|
||||
deleted: zod.boolean().readonly().optional(),
|
||||
|
||||
});
|
||||
|
||||
export type UUIDGenericDataSoftDelete = zod.infer<typeof ZodUUIDGenericDataSoftDelete>;
|
||||
|
||||
export function isUUIDGenericDataSoftDelete(data: any): data is UUIDGenericDataSoftDelete {
|
||||
try {
|
||||
ZodUUIDGenericDataSoftDelete.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUUIDGenericDataSoftDelete' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUUIDGenericDataSoftDeleteWrite = ZodUUIDGenericDataWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type UUIDGenericDataSoftDeleteWrite = zod.infer<typeof ZodUUIDGenericDataSoftDeleteWrite>;
|
||||
|
||||
export function isUUIDGenericDataSoftDeleteWrite(data: any): data is UUIDGenericDataSoftDeleteWrite {
|
||||
try {
|
||||
ZodUUIDGenericDataSoftDeleteWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUUIDGenericDataSoftDeleteWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
42
front/src/app/back-api/model/uuid-generic-data.ts
Normal file
42
front/src/app/back-api/model/uuid-generic-data.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Interface of the server (auto-generated code)
|
||||
*/
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodGenericTiming, ZodGenericTimingWrite } from "./generic-timing";
|
||||
|
||||
export const ZodUUIDGenericData = ZodGenericTiming.extend({
|
||||
/**
|
||||
* Unique UUID of the object
|
||||
*/
|
||||
uuid: ZodUUID.readonly(),
|
||||
|
||||
});
|
||||
|
||||
export type UUIDGenericData = zod.infer<typeof ZodUUIDGenericData>;
|
||||
|
||||
export function isUUIDGenericData(data: any): data is UUIDGenericData {
|
||||
try {
|
||||
ZodUUIDGenericData.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUUIDGenericData' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export const ZodUUIDGenericDataWrite = ZodGenericTimingWrite.extend({
|
||||
|
||||
});
|
||||
|
||||
export type UUIDGenericDataWrite = zod.infer<typeof ZodUUIDGenericDataWrite>;
|
||||
|
||||
export function isUUIDGenericDataWrite(data: any): data is UUIDGenericDataWrite {
|
||||
try {
|
||||
ZodUUIDGenericDataWrite.parse(data);
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
console.log(`Fail to parse data type='ZodUUIDGenericDataWrite' error=${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
import { z as zod } from "zod";
|
||||
|
||||
import {ZodObjectId} from "./object-id";
|
||||
import {ZodUUID} from "./uuid";
|
||||
import {ZodLong} from "./long";
|
||||
import {ZodInteger} from "./integer";
|
||||
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
|
||||
@ -21,6 +22,7 @@ export const ZodMedia = ZodGenericDataSoftDelete.extend({
|
||||
* Foreign Key Id of the data
|
||||
*/
|
||||
dataId: ZodObjectId,
|
||||
dataIdOld: ZodUUID,
|
||||
/**
|
||||
* Type of the media
|
||||
*/
|
||||
@ -77,6 +79,7 @@ export const ZodMediaWrite = ZodGenericDataSoftDeleteWrite.extend({
|
||||
* Foreign Key Id of the data
|
||||
*/
|
||||
dataId: ZodObjectId.optional(),
|
||||
dataIdOld: ZodUUID.optional(),
|
||||
/**
|
||||
* Type of the media
|
||||
*/
|
||||
|
43
front/src/components/Vignette.tsx
Normal file
43
front/src/components/Vignette.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Center, Flex, FlexProps, Text } from '@chakra-ui/react';
|
||||
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
|
||||
export type VignetteProps = { text?: String; icon: ReactNode } & Pick<
|
||||
FlexProps,
|
||||
'onClick'
|
||||
>;
|
||||
export const Vignette = ({ icon, text, ...rest }: VignetteProps) => {
|
||||
return (
|
||||
<Flex
|
||||
align="flex-start"
|
||||
width="200px"
|
||||
height="190px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Flex direction="column" width="full" height="full">
|
||||
<Center height="full">{icon}</Center>
|
||||
<Center>
|
||||
<Text
|
||||
fontSize="25px"
|
||||
fontWeight="bold"
|
||||
textTransform="uppercase"
|
||||
userSelect="none"
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
61
front/src/components/VignetteDetail.tsx
Normal file
61
front/src/components/VignetteDetail.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Flex, FlexProps } from '@chakra-ui/react';
|
||||
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
|
||||
export type VignetteProps = { text?: String; icon: ReactNode } & Pick<
|
||||
FlexProps,
|
||||
'onClick'
|
||||
>;
|
||||
export const VignetteDetail = ({ icon, text, ...rest }: VignetteProps) => {
|
||||
return (
|
||||
<Flex
|
||||
align="flex-start"
|
||||
width="270px"
|
||||
height="120px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
onClick={() => onSelectItem(data.id)}
|
||||
>
|
||||
<DisplayType dataType={data} />
|
||||
</Flex>
|
||||
// <Flex
|
||||
// align="flex-start"
|
||||
// width="200px"
|
||||
// height="190px"
|
||||
// border="1px"
|
||||
// borderColor="brand.900"
|
||||
// backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
// padding="5px"
|
||||
// as="button"
|
||||
// _hover={{
|
||||
// boxShadow: 'outline-over',
|
||||
// bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
// }}
|
||||
// {...rest}
|
||||
// >
|
||||
// <Flex direction="column" width="full" height="full">
|
||||
// <Center height="full">{icon}</Center>
|
||||
// <Center>
|
||||
// <Text
|
||||
// fontSize="25px"
|
||||
// fontWeight="bold"
|
||||
// textTransform="uppercase"
|
||||
// userSelect="none"
|
||||
// >
|
||||
// {text}
|
||||
// </Text>
|
||||
// </Center>
|
||||
// </Flex>
|
||||
// </Flex>
|
||||
);
|
||||
};
|
@ -25,15 +25,15 @@ export const DisplayMediaFull = ({
|
||||
const { dataSeries } = useSpecificSeries(media?.seriesId);
|
||||
return (
|
||||
<Flex
|
||||
direction="row"
|
||||
direction="column"
|
||||
width="full"
|
||||
height="full"
|
||||
data-testid="display-Media-full"
|
||||
>
|
||||
<Covers
|
||||
data={media?.covers}
|
||||
size="60px"
|
||||
marginY="auto"
|
||||
size="100"
|
||||
height="full"
|
||||
iconEmpty={MediaActive?.id === media.id ? <LuPlay /> : <LuMusic2 />}
|
||||
onClick={onClick}
|
||||
/>
|
||||
@ -51,21 +51,21 @@ export const DisplayMediaFull = ({
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
marginX="auto"
|
||||
overflow="hidden"
|
||||
// TODO: noOfLines={1}
|
||||
color={MediaActive?.id === media.id ? 'green.700' : undefined}
|
||||
>
|
||||
{media.name} {media.episode && ` [${media.episode}]`}
|
||||
</Text>
|
||||
{dataSeason && (
|
||||
{/* {dataSeason && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
fontSize="15px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
marginX="auto"
|
||||
overflow="hidden"
|
||||
//noOfLines={1}
|
||||
marginY="auto"
|
||||
@ -76,8 +76,8 @@ export const DisplayMediaFull = ({
|
||||
</Text>{' '}
|
||||
{dataSeason.name}
|
||||
</Text>
|
||||
)}
|
||||
{dataSeries && (
|
||||
)} */}
|
||||
{/* {dataSeries && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
@ -95,8 +95,8 @@ export const DisplayMediaFull = ({
|
||||
</Text>{' '}
|
||||
{dataSeries && dataSeries.name}
|
||||
</Text>
|
||||
)}
|
||||
{dataType && (
|
||||
)} */}
|
||||
{/* {dataType && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
@ -114,7 +114,7 @@ export const DisplayMediaFull = ({
|
||||
</Text>{' '}
|
||||
{dataType.name}
|
||||
</Text>
|
||||
)}
|
||||
)} */}
|
||||
</Flex>
|
||||
<ContextMenu
|
||||
elements={contextMenu}
|
||||
|
126
front/src/components/media/DisplayMediaListFull.tsx
Normal file
126
front/src/components/media/DisplayMediaListFull.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { LuMusic2, LuPlay } from 'react-icons/lu';
|
||||
|
||||
import { Media } from '@/back-api';
|
||||
import { Covers } from '@/components/Cover';
|
||||
import { ContextMenu, MenuElement } from '@/components/contextMenu/ContextMenu';
|
||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useSpecificSeason } from '@/service/Season';
|
||||
import { useSpecificSeries } from '@/service/Series';
|
||||
import { useSpecificType } from '@/service/Type';
|
||||
|
||||
export type DisplayMediaProps = {
|
||||
media: Media;
|
||||
onClick?: () => void;
|
||||
contextMenu?: MenuElement[];
|
||||
};
|
||||
export const DisplayMediaListFull = ({
|
||||
media,
|
||||
onClick,
|
||||
contextMenu,
|
||||
}: DisplayMediaProps) => {
|
||||
const { MediaActive } = useActivePlaylistService();
|
||||
const { dataSeason } = useSpecificSeason(media?.seasonId);
|
||||
const { dataType } = useSpecificType(media?.typeId);
|
||||
const { dataSeries } = useSpecificSeries(media?.seriesId);
|
||||
return (
|
||||
<Flex
|
||||
direction="row"
|
||||
width="full"
|
||||
height="full"
|
||||
data-testid="display-Media-full"
|
||||
>
|
||||
<Covers
|
||||
data={media?.covers}
|
||||
size="50px"
|
||||
marginY="auto"
|
||||
height="full"
|
||||
iconEmpty={MediaActive?.id === media.id ? <LuPlay /> : <LuMusic2 />}
|
||||
onClick={onClick}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
width="full"
|
||||
height="full"
|
||||
paddingLeft="5px"
|
||||
overflowX="hidden"
|
||||
onClick={onClick}
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
// TODO: noOfLines={1}
|
||||
color={MediaActive?.id === media.id ? 'green.700' : undefined}
|
||||
>
|
||||
{media.episode && `[${media.episode}]`} {media.name}
|
||||
</Text>
|
||||
{/* {dataSeason && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
fontSize="15px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginX="auto"
|
||||
overflow="hidden"
|
||||
//noOfLines={1}
|
||||
marginY="auto"
|
||||
color={MediaActive?.id === media.id ? 'green.700' : undefined}
|
||||
>
|
||||
<Text as="span" fontWeight="normal">
|
||||
Season:
|
||||
</Text>{' '}
|
||||
{dataSeason.name}
|
||||
</Text>
|
||||
)} */}
|
||||
{/* {dataSeries && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
fontSize="15px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
//noOfLines={1}
|
||||
marginY="auto"
|
||||
color={MediaActive?.id === media.id ? 'green.700' : undefined}
|
||||
>
|
||||
<Text as="span" fontWeight="normal">
|
||||
Series(s):
|
||||
</Text>{' '}
|
||||
{dataSeries && dataSeries.name}
|
||||
</Text>
|
||||
)} */}
|
||||
{/* {dataType && (
|
||||
<Text
|
||||
as="span"
|
||||
alignContent="left"
|
||||
fontSize="15px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
overflow="hidden"
|
||||
//noOfLines={1}
|
||||
marginY="auto"
|
||||
color={MediaActive?.id === media.id ? 'green.700' : undefined}
|
||||
>
|
||||
<Text as="span" fontWeight="normal">
|
||||
Type:
|
||||
</Text>{' '}
|
||||
{dataType.name}
|
||||
</Text>
|
||||
)} */}
|
||||
</Flex>
|
||||
<ContextMenu
|
||||
elements={contextMenu}
|
||||
data-testid="display-Media-full_context-menu"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -18,7 +18,7 @@ export const DisplaySeries = ({ dataSeries }: DisplaySeriesProps) => {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Flex direction="row" width="full" height="full">
|
||||
<Flex direction="column" width="full" height="full">
|
||||
<Covers
|
||||
data={dataSeries?.covers}
|
||||
size="100"
|
||||
@ -27,11 +27,11 @@ export const DisplaySeries = ({ dataSeries }: DisplaySeriesProps) => {
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
width="150px"
|
||||
maxWidth="150px"
|
||||
width="full"
|
||||
height="full"
|
||||
paddingLeft="5px"
|
||||
overflowX="hidden"
|
||||
alignContent="center"
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
@ -39,7 +39,7 @@ export const DisplaySeries = ({ dataSeries }: DisplaySeriesProps) => {
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
marginX="auto"
|
||||
overflow="hidden"
|
||||
//TODO: noOfLines={[1, 2]}
|
||||
>
|
||||
@ -50,7 +50,7 @@ export const DisplaySeries = ({ dataSeries }: DisplaySeriesProps) => {
|
||||
alignContent="left"
|
||||
fontSize="15px"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
marginX="auto"
|
||||
overflow="hidden"
|
||||
//TODO: noOfLines={1}
|
||||
>
|
||||
|
@ -18,10 +18,10 @@ export const DisplayType = ({ dataType }: DisplayTypeProps) => {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Flex direction="row" width="full" height="full">
|
||||
<Flex direction="column" width="full" height="full">
|
||||
<Covers
|
||||
data={dataType?.covers}
|
||||
size="100"
|
||||
size="50"
|
||||
height="full"
|
||||
iconEmpty={<LuDisc3 />}
|
||||
/>
|
||||
|
@ -16,7 +16,7 @@ export interface Environment {
|
||||
const serverSSOAddress = 'https://atria-soft.org';
|
||||
|
||||
const environment_back_prod: Environment = {
|
||||
production: false,
|
||||
production: true,
|
||||
// URL of development API
|
||||
applName: 'karideo',
|
||||
defaultServer: 'karideo',
|
||||
@ -45,42 +45,9 @@ const environment_local: Environment = {
|
||||
ssoSignUp: `${serverSSOAddress}/karso/signup/karideo-dev/`,
|
||||
ssoSignOut: `${serverSSOAddress}/karso/signout/karideo-dev/`,
|
||||
tokenStoredInPermanentStorage: false,
|
||||
replaceDataToRealServer: true,
|
||||
replaceDataToRealServer: false,
|
||||
};
|
||||
|
||||
const environment_full_local: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karideo',
|
||||
defaultServer: 'karideo',
|
||||
server: {
|
||||
karideo: 'http://localhost:18080/karideo/api',
|
||||
karso: 'http://localhost:15080/karso/api',
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: 'http://localhost:4200/signin/karideo-dev/',
|
||||
ssoSignUp: 'http://localhost:4200/signup/karideo-dev/',
|
||||
ssoSignOut: 'http://localhost:4200/signout/karideo-dev/',
|
||||
tokenStoredInPermanentStorage: false,
|
||||
};
|
||||
|
||||
const environment_hybrid: Environment = {
|
||||
production: false,
|
||||
// URL of development API
|
||||
applName: 'karideo',
|
||||
defaultServer: 'karideo',
|
||||
server: {
|
||||
karideo: `${serverSSOAddress}/karideo/api`,
|
||||
karso: `${serverSSOAddress}/karso/api`,
|
||||
},
|
||||
ssoSite: `${serverSSOAddress}/karso/`,
|
||||
ssoSignIn: 'http://localhost:4200/signin/karideo-dev/',
|
||||
ssoSignUp: 'http://localhost:4200/signup/karideo-dev/',
|
||||
ssoSignOut: 'http://localhost:4200/signout/karideo-dev/',
|
||||
tokenStoredInPermanentStorage: false,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if the current environment is for development
|
||||
* @returns true if development is active.
|
||||
|
@ -2,7 +2,6 @@ import { StrictMode } from 'react';
|
||||
|
||||
import { ChakraProvider } from '@chakra-ui/react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
import App from '@/App';
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
import { Center, Flex, HStack, Text } from '@chakra-ui/react';
|
||||
import { LuCrown, LuDisc3, LuEar, LuFileAudio } from 'react-icons/lu';
|
||||
import { MdGroup } from 'react-icons/md';
|
||||
import { HStack } from '@chakra-ui/react';
|
||||
import { LuCloudMoon, LuCrown } from 'react-icons/lu';
|
||||
import { MdVideoFile } from 'react-icons/md';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { Vignette } from '@/components/Vignette';
|
||||
|
||||
type HomeListType = {
|
||||
id: number;
|
||||
@ -24,28 +24,16 @@ const homeList: HomeListType[] = [
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Series',
|
||||
icon: <MdGroup style={{ width: '100px', height: '100px' }} />,
|
||||
name: 'Univers',
|
||||
icon: <LuCloudMoon style={{ width: '100px', height: '100px' }} />,
|
||||
to: '/Series',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Seasons',
|
||||
icon: <LuDisc3 style={{ width: '100px', height: '100px' }} />,
|
||||
to: '/Season',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Medias',
|
||||
icon: <LuFileAudio style={{ width: '100px', height: '100px' }} />,
|
||||
icon: <MdVideoFile style={{ width: '100px', height: '100px' }} />,
|
||||
to: '/Media',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Playlists',
|
||||
icon: <LuEar style={{ width: '100px', height: '100px' }} />,
|
||||
to: '/playlists',
|
||||
},
|
||||
];
|
||||
|
||||
export const HomePage = () => {
|
||||
@ -65,36 +53,12 @@ export const HomePage = () => {
|
||||
justify="center"
|
||||
>
|
||||
{homeList.map((data) => (
|
||||
<Flex
|
||||
align="flex-start"
|
||||
width="200px"
|
||||
height="190px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
<Vignette
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
onClick={() => onSelectItem(data)}
|
||||
>
|
||||
<Flex direction="column" width="full" height="full">
|
||||
<Center height="full">{data.icon}</Center>
|
||||
<Center>
|
||||
<Text
|
||||
fontSize="25px"
|
||||
fontWeight="bold"
|
||||
textTransform="uppercase"
|
||||
userSelect="none"
|
||||
>
|
||||
{data.name}
|
||||
</Text>
|
||||
</Center>
|
||||
</Flex>
|
||||
</Flex>
|
||||
text={data.name}
|
||||
icon={data.icon}
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
</PageLayout>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||
import { Box, Button, Flex, HStack, Text } from '@chakra-ui/react';
|
||||
import { LuDisc3 } from 'react-icons/lu';
|
||||
import { MdEdit } from 'react-icons/md';
|
||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||
@ -14,18 +14,22 @@ import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
|
||||
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||
import { DisplaySeries } from '@/components/series/DisplaySeries';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { useActivePlaylistService, useSeriesWithType, useSpecificType, useTypeGetVideo } from '@/service';
|
||||
import {
|
||||
useActivePlaylistService,
|
||||
useSeriesWithType,
|
||||
useSpecificType,
|
||||
useTypeGetVideo,
|
||||
} from '@/service';
|
||||
|
||||
export const TypeDetailPage = () => {
|
||||
const { TypeId } = useParams();
|
||||
const TypeIdInt = TypeId ? parseInt(TypeId, 10) : undefined;
|
||||
const { typeId } = useParams();
|
||||
const typeIdInt = typeId ? parseInt(typeId, 10) : undefined;
|
||||
const { playInList } = useActivePlaylistService();
|
||||
const { dataType } = useSpecificType(TypeIdInt);
|
||||
const { videoWithType } = useTypeGetVideo(TypeIdInt);
|
||||
const { seriesWithType } = useSeriesWithType(TypeIdInt);
|
||||
const { dataType } = useSpecificType(typeIdInt);
|
||||
const { videoWithType } = useTypeGetVideo(typeIdInt);
|
||||
const { seriesWithType } = useSeriesWithType(typeIdInt);
|
||||
const navigate = useNavigate();
|
||||
const onSelectItem = (MediaId: number) => {
|
||||
//navigate(`/Series/${SeriesIdInt}/Type/${TypeId}`);
|
||||
const onSelectItem = (mediaId: number) => {
|
||||
let currentPlay = 0;
|
||||
const listMediaId: number[] = [];
|
||||
if (!videoWithType) {
|
||||
@ -34,12 +38,15 @@ export const TypeDetailPage = () => {
|
||||
}
|
||||
for (let iii = 0; iii < videoWithType.length; iii++) {
|
||||
listMediaId.push(videoWithType[iii].id);
|
||||
if (videoWithType[iii].id === MediaId) {
|
||||
if (videoWithType[iii].id === mediaId) {
|
||||
currentPlay = iii;
|
||||
}
|
||||
}
|
||||
playInList(currentPlay, listMediaId);
|
||||
};
|
||||
const onSelectSeriesItem = (seriesId: number) => {
|
||||
navigate(`/type/${typeId}/series/${seriesId}`);
|
||||
};
|
||||
|
||||
console.log(`dataType = ${JSON.stringify(dataType, null, 2)}`);
|
||||
if (!dataType) {
|
||||
@ -47,7 +54,7 @@ export const TypeDetailPage = () => {
|
||||
<>
|
||||
<TopBar title="Type detail" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load Series id: {TypeId}
|
||||
Fail to load Series id: {typeId}
|
||||
</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
@ -57,9 +64,7 @@ export const TypeDetailPage = () => {
|
||||
<TopBar title="Type detail">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
navigate(`/Type/${TypeId}/edit-Type/${TypeId}`)
|
||||
}
|
||||
onClick={() => navigate(`/Type/${typeId}/edit-type/${typeId}`)}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
@ -83,36 +88,36 @@ export const TypeDetailPage = () => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
direction="column"
|
||||
<HStack
|
||||
wrap="wrap"
|
||||
gap="20px"
|
||||
marginX="auto"
|
||||
padding="20px"
|
||||
width="80%"
|
||||
justify="center"
|
||||
>
|
||||
{seriesWithType?.map((data) => (
|
||||
<Box
|
||||
minWidth="100%"
|
||||
//height="60px"
|
||||
width="200px"
|
||||
height="280px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
onClick={() => onSelectSeriesItem(data.id)}
|
||||
>
|
||||
<DisplaySeries
|
||||
dataSeries={data}
|
||||
//onClick={() => onSelectItem(data.id)}
|
||||
// >onClick={() => onSelectItem(data.id)}
|
||||
// contextMenu={[
|
||||
// {
|
||||
// name: 'Edit',
|
||||
// onClick: () => {
|
||||
// navigate(`/Type/${TypeId}/edit-Media/${data.id}`);
|
||||
// navigate(`/Type/${TypeId}/edit-media/${data.id}`);
|
||||
// },
|
||||
// },
|
||||
// { name: 'Add Playlist', onClick: () => {} },
|
||||
@ -122,12 +127,11 @@ export const TypeDetailPage = () => {
|
||||
))}
|
||||
{videoWithType?.map((data) => (
|
||||
<Box
|
||||
minWidth="100%"
|
||||
//height="60px"
|
||||
width="200px"
|
||||
height="280px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
@ -142,7 +146,7 @@ export const TypeDetailPage = () => {
|
||||
{
|
||||
name: 'Edit',
|
||||
onClick: () => {
|
||||
navigate(`/Type/${TypeId}/edit-Media/${data.id}`);
|
||||
navigate(`/Type/${typeId}/edit-media/${data.id}`);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
@ -151,10 +155,10 @@ export const TypeDetailPage = () => {
|
||||
</Box>
|
||||
))}
|
||||
<EmptyEnd />
|
||||
</Flex>
|
||||
</HStack>
|
||||
<Routes>
|
||||
<Route path="edit-Media/:MediaId" element={<MediaEditPopUp />} />
|
||||
<Route path="edit-Type/:TypeId" element={<TypeEditPopUp />} />
|
||||
<Route path="edit-media/:MediaId" element={<MediaEditPopUp />} />
|
||||
<Route path="edit-type/:TypeId" element={<TypeEditPopUp />} />
|
||||
</Routes>
|
||||
</PageLayout>
|
||||
</>
|
||||
|
@ -4,11 +4,11 @@ import { Flex, HStack } from '@chakra-ui/react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||
import { DisplayType } from '@/components/type/DisplayType';
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||
import { SearchInput } from '@/components/SearchInput';
|
||||
import { TopBar } from '@/components/TopBar/TopBar';
|
||||
import { DisplayType } from '@/components/type/DisplayType';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { useOrderedTypes } from '@/service/Type';
|
||||
|
||||
@ -45,12 +45,11 @@ export const TypesPage = () => {
|
||||
{dataTypes.map((data) => (
|
||||
<Flex
|
||||
align="flex-start"
|
||||
width="270px"
|
||||
height="120px"
|
||||
width="200px"
|
||||
height="280px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
|
@ -1,15 +1,26 @@
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { Error404 } from '@/errors';
|
||||
import { TypesSeriesDetailPage } from '@/scene/type/TypesSeriesDetailPage';
|
||||
|
||||
import { TypeDetailPage } from './TypesDetailPage';
|
||||
import { TypesPage } from './TypesPage';
|
||||
import { TypesSeriesSeasonDetailPage } from './TypesSeriesSeasonDetailPage';
|
||||
|
||||
export const TypeRoutes = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to="all" replace />} />
|
||||
<Route path="all" element={<TypesPage />} />
|
||||
<Route path=":TypeId/*" element={<TypeDetailPage />} />
|
||||
<Route path=":typeId/*" element={<TypeDetailPage />} />
|
||||
<Route
|
||||
path=":typeId/series/:seriesId/*"
|
||||
element={<TypesSeriesDetailPage />}
|
||||
/>
|
||||
<Route
|
||||
path=":typeId/series/:seriesId/season/:seasonId/*"
|
||||
element={<TypesSeriesSeasonDetailPage />}
|
||||
/>
|
||||
<Route path="*" element={<Error404 />} />
|
||||
</Routes>
|
||||
);
|
||||
|
190
front/src/scene/type/TypesSeriesDetailPage.tsx
Normal file
190
front/src/scene/type/TypesSeriesDetailPage.tsx
Normal file
@ -0,0 +1,190 @@
|
||||
import { Box, Button, Flex, HStack, Text } from '@chakra-ui/react';
|
||||
import { LuDisc3 } from 'react-icons/lu';
|
||||
import { MdEdit } from 'react-icons/md';
|
||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { Covers } from '@/components/Cover';
|
||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
||||
//import { useMediasOfAType } from '@/service/Media';
|
||||
import { DisplayMediaFull } from '@/components/media/DisplayMediaFull';
|
||||
import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
|
||||
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||
import { DisplaySeason } from '@/components/season/DisplaySeason';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import {
|
||||
useActivePlaylistService,
|
||||
useSeasonWithSeries,
|
||||
useSpecificSeries,
|
||||
useSpecificType,
|
||||
useTypeSeriesGetVideo,
|
||||
} from '@/service';
|
||||
|
||||
export const TypesSeriesDetailPage = () => {
|
||||
const { typeId, seriesId } = useParams();
|
||||
const typeIdInt = typeId ? parseInt(typeId, 10) : undefined;
|
||||
const seriesIdInt = seriesId ? parseInt(seriesId, 10) : undefined;
|
||||
const { playInList } = useActivePlaylistService();
|
||||
const { dataType } = useSpecificType(typeIdInt);
|
||||
const { dataSeries } = useSpecificSeries(seriesIdInt);
|
||||
const { videoWithType } = useTypeSeriesGetVideo(typeIdInt, seriesIdInt);
|
||||
const { seasonOfSeriesId } = useSeasonWithSeries(/*typeIdInt,*/ seriesIdInt);
|
||||
const navigate = useNavigate();
|
||||
const onSelectSeasonItem = (mediaId: number) => {
|
||||
navigate(`/type/${typeId}/series/${seriesId}/season/${mediaId}`);
|
||||
}
|
||||
const onSelectItem = (mediaId: number) => {
|
||||
let currentPlay = 0;
|
||||
const listMediaId: number[] = [];
|
||||
if (!videoWithType) {
|
||||
console.log('Fail to get Type...');
|
||||
return;
|
||||
}
|
||||
for (let iii = 0; iii < videoWithType.length; iii++) {
|
||||
listMediaId.push(videoWithType[iii].id);
|
||||
if (videoWithType[iii].id === mediaId) {
|
||||
currentPlay = iii;
|
||||
}
|
||||
}
|
||||
playInList(currentPlay, listMediaId);
|
||||
};
|
||||
|
||||
console.log(`dataType = ${JSON.stringify(dataType, null, 2)}`);
|
||||
if (!dataType) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Type detail" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load Series id: {typeId}/{seriesId}
|
||||
</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Series detail">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
navigate(
|
||||
`/Type/${typeId}/series/${seriesId}/edit-series/${seriesId}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<Flex
|
||||
direction="row"
|
||||
width="80%"
|
||||
marginX="auto"
|
||||
padding="10px"
|
||||
gap="10px"
|
||||
>
|
||||
<Covers data={dataType?.covers} iconEmpty={<LuDisc3 />} slideshow />
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
type: {dataType?.name}
|
||||
</Text>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
series: {dataSeries?.name}
|
||||
</Text>
|
||||
{dataSeries?.description && (
|
||||
<Text>Description: {dataSeries?.description}</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<HStack
|
||||
wrap="wrap"
|
||||
gap="20px"
|
||||
marginX="auto"
|
||||
padding="20px"
|
||||
justify="center"
|
||||
>
|
||||
{seasonOfSeriesId?.map((data) => (
|
||||
<Box
|
||||
key={data.id}
|
||||
width="full"
|
||||
maxWidth="calc(min(450px,80%))"
|
||||
height="150px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
onClick={() => onSelectSeasonItem(data.id)}
|
||||
>
|
||||
<DisplaySeason
|
||||
dataSeason={data}
|
||||
// contextMenu={[
|
||||
// {
|
||||
// name: 'Edit',
|
||||
// onClick: () => {
|
||||
// navigate(`/Type/${TypeId}/edit-media/${data.id}`);
|
||||
// },
|
||||
// },
|
||||
// { name: 'Add Playlist', onClick: () => {} },
|
||||
// ]}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</HStack>
|
||||
<Box width="full" height="10px" background="red"/>
|
||||
<HStack
|
||||
wrap="wrap"
|
||||
gap="20px"
|
||||
marginX="auto"
|
||||
padding="20px"
|
||||
justify="center"
|
||||
>
|
||||
{videoWithType?.map((data) => (
|
||||
<Box
|
||||
key={data.id}
|
||||
width="200px"
|
||||
height="280px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
>
|
||||
<DisplayMediaFull
|
||||
media={data}
|
||||
onClick={() => onSelectItem(data.id)}
|
||||
contextMenu={[
|
||||
{
|
||||
name: 'Edit',
|
||||
onClick: () => {
|
||||
navigate(
|
||||
`/type/${typeId}/series/${seriesId}/edit-media/${data.id}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
<EmptyEnd />
|
||||
</HStack>
|
||||
<Routes>
|
||||
<Route path="edit-media/:mediaId" element={<MediaEditPopUp />} />
|
||||
<Route path="edit-type/:typeId" element={<TypeEditPopUp />} />
|
||||
{/* <Route path="edit-season/:seasonId" element={<TypeSeasonPopUp />} /> */}
|
||||
</Routes>
|
||||
</PageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
150
front/src/scene/type/TypesSeriesSeasonDetailPage.tsx
Normal file
150
front/src/scene/type/TypesSeriesSeasonDetailPage.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
import { Box, Button, Flex, HStack, Text } from '@chakra-ui/react';
|
||||
import { LuDisc3 } from 'react-icons/lu';
|
||||
import { MdEdit } from 'react-icons/md';
|
||||
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { Covers } from '@/components/Cover';
|
||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
|
||||
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
|
||||
import { DisplayMediaFull } from '@/components/media/DisplayMediaFull';
|
||||
import { TypeEditPopUp } from '@/components/popup/GenderEditPopUp';
|
||||
import { MediaEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import {
|
||||
useActivePlaylistService,
|
||||
useSpecificSeason,
|
||||
useSpecificSeries,
|
||||
useSpecificType,
|
||||
useTypeSeriesSeasonGetVideo,
|
||||
} from '@/service';
|
||||
import { DisplayMediaListFull } from '@/components/media/DisplayMediaListFull';
|
||||
|
||||
export const TypesSeriesSeasonDetailPage = () => {
|
||||
const { typeId, seriesId, seasonId } = useParams();
|
||||
const typeIdInt = typeId ? parseInt(typeId, 10) : undefined;
|
||||
const seriesIdInt = seriesId ? parseInt(seriesId, 10) : undefined;
|
||||
const seasonIdInt = seasonId ? parseInt(seasonId, 10) : undefined;
|
||||
const { playInList } = useActivePlaylistService();
|
||||
const { dataType } = useSpecificType(typeIdInt);
|
||||
const { dataSeries } = useSpecificSeries(seriesIdInt);
|
||||
const { dataSeason } = useSpecificSeason(seasonIdInt);
|
||||
const { videoWithType: videos } = useTypeSeriesSeasonGetVideo(typeIdInt, seriesIdInt, seasonIdInt);
|
||||
const navigate = useNavigate();
|
||||
const onSelectItem = (mediaId: number) => {
|
||||
let currentPlay = 0;
|
||||
const listMediaId: number[] = [];
|
||||
if (!videos) {
|
||||
console.log('Fail to get Type...');
|
||||
return;
|
||||
}
|
||||
for (let iii = 0; iii < videos.length; iii++) {
|
||||
listMediaId.push(videos[iii].id);
|
||||
if (videos[iii].id === mediaId) {
|
||||
currentPlay = iii;
|
||||
}
|
||||
}
|
||||
playInList(currentPlay, listMediaId);
|
||||
};
|
||||
|
||||
console.log(`dataType = ${JSON.stringify(dataType, null, 2)}`);
|
||||
if (!dataType) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Type detail" />
|
||||
<PageLayoutInfoCenter>
|
||||
Fail to load Series id: {typeId}/{seriesId}/{seasonId}
|
||||
</PageLayoutInfoCenter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<TopBar title="season detail">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={() =>
|
||||
navigate(
|
||||
`/Type/${typeId}/series/${seriesId}/season/${seasonId}/edit-season/${seasonId}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<Flex
|
||||
direction="row"
|
||||
width="80%"
|
||||
marginX="auto"
|
||||
padding="10px"
|
||||
gap="10px"
|
||||
>
|
||||
<Covers data={dataType?.covers} iconEmpty={<LuDisc3 />} slideshow />
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
type: {dataType?.name}
|
||||
</Text>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
series: {dataSeries?.name}
|
||||
</Text>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
season: {dataSeason?.name}
|
||||
</Text>
|
||||
{dataSeason?.description && (
|
||||
<Text>Description: {dataSeason?.description}</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<HStack
|
||||
wrap="wrap"
|
||||
gap="20px"
|
||||
marginX="auto"
|
||||
padding="20px"
|
||||
justify="center"
|
||||
>
|
||||
{videos?.map((data) => (
|
||||
<Box
|
||||
key={data.id}
|
||||
width="calc(max(300px,50%))"
|
||||
height="60px"
|
||||
border="1px"
|
||||
borderColor="brand.900"
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
}}
|
||||
>
|
||||
<DisplayMediaListFull
|
||||
media={data}
|
||||
onClick={() => onSelectItem(data.id)}
|
||||
contextMenu={[
|
||||
{
|
||||
name: 'Edit',
|
||||
onClick: () => {
|
||||
navigate(
|
||||
`/type/${typeId}/series/${seriesId}/season/${seasonId}/edit-media/${data.id}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => {} },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
<EmptyEnd />
|
||||
</HStack>
|
||||
<Routes>
|
||||
<Route path="edit-media/:mediaId" element={<MediaEditPopUp />} />
|
||||
<Route path="edit-type/:typeId" element={<TypeEditPopUp />} />
|
||||
{/* <Route path="edit-season/:seasonId" element={<TypeSeasonPopUp />} /> */}
|
||||
</Routes>
|
||||
</PageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
@ -6,7 +6,9 @@ import { SessionServiceProps } from '@/service/session';
|
||||
import { DataStoreType, useDataStore } from '@/utils/data-store';
|
||||
import { DataTools, TypeCheck } from '@/utils/data-tools';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
|
||||
import { useMediaService } from './Media';
|
||||
import { useSeasonService } from './Season';
|
||||
|
||||
export type SeriesServiceProps = {
|
||||
store: DataStoreType<Series>;
|
||||
@ -67,7 +69,6 @@ export const useSpecificSeries = (id: number | undefined) => {
|
||||
return { dataSeries };
|
||||
};
|
||||
|
||||
|
||||
export const useSeriesVideo = (id: number) => {
|
||||
const { store } = useSeriesService();
|
||||
const seriesVideo = useMemo(() => {
|
||||
@ -78,7 +79,8 @@ export const useSeriesVideo = (id: number) => {
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: id,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seasonId',
|
||||
value: undefined,
|
||||
@ -96,41 +98,47 @@ export const useSeriesCountVideo = (id?: number) => {
|
||||
if (id === undefined) {
|
||||
return 0;
|
||||
}
|
||||
return DataTools.count(
|
||||
store.data,
|
||||
[
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: id,
|
||||
},
|
||||
]
|
||||
);
|
||||
return DataTools.count(store.data, [
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: id,
|
||||
},
|
||||
]);
|
||||
}, [store.data, id]);
|
||||
return { isLoading: store.isLoading, seriesCountVideo };
|
||||
};
|
||||
export const useSeriesSeasons = (id: number) => {
|
||||
const { store } = useSeriesService();
|
||||
const seriesSeason = useMemo(() => {
|
||||
export const useSeasonWithSeries = (seriesId?: number) => {
|
||||
const { store } = useSeasonService();
|
||||
const seasonOfSeriesId = useMemo(() => {
|
||||
if (seriesId === undefined) {
|
||||
return [];
|
||||
}
|
||||
return DataTools.getsWhere(
|
||||
store.data,
|
||||
[
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'parentId',
|
||||
value: id,
|
||||
value: seriesId,
|
||||
},
|
||||
], ['id']
|
||||
],
|
||||
['name', 'id']
|
||||
// TODO: ['>name', 'id'] padding right request ...:
|
||||
// const maxLength = Math.max(...values.map(v => v.length));
|
||||
// const paddedA = a.padStart(maxLength, " ");
|
||||
// const paddedB = b.padStart(maxLength, " ");
|
||||
// return paddedA.localeCompare(paddedB);
|
||||
);
|
||||
}, [store.data, id]);
|
||||
return { isLoading: store.isLoading, dataTypes: seriesSeason };
|
||||
}, [store.data, seriesId]);
|
||||
return { isLoading: store.isLoading, seasonOfSeriesId };
|
||||
};
|
||||
|
||||
export const useSeriesWithType = (typeId?: number) => {
|
||||
const { store } = useSeriesService();
|
||||
const seriesWithType = useMemo(() => {
|
||||
if (typeId === undefined) {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
return DataTools.getsWhere(
|
||||
store.data,
|
||||
@ -139,10 +147,10 @@ export const useSeriesWithType = (typeId?: number) => {
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'parentId',
|
||||
value: typeId,
|
||||
}],
|
||||
},
|
||||
],
|
||||
['name']
|
||||
);
|
||||
}, [store.data, typeId]);
|
||||
return { isLoading: store.isLoading, seriesWithType };
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { SessionServiceProps } from '@/service/session';
|
||||
import { DataStoreType, useDataStore } from '@/utils/data-store';
|
||||
import { DataTools, TypeCheck } from '@/utils/data-tools';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
|
||||
import { useMediaService } from './Media';
|
||||
|
||||
export type TypeServiceProps = {
|
||||
@ -72,15 +73,13 @@ export const useTypeCountVideo = (id?: number) => {
|
||||
if (id === undefined) {
|
||||
return 0;
|
||||
}
|
||||
return DataTools.count(
|
||||
store.data,
|
||||
[
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'typeId',
|
||||
value: id,
|
||||
}]
|
||||
);
|
||||
return DataTools.count(store.data, [
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'typeId',
|
||||
value: id,
|
||||
},
|
||||
]);
|
||||
}, [store.data, id]);
|
||||
return { isLoading: store.isLoading, countVideoWithType };
|
||||
};
|
||||
@ -98,18 +97,101 @@ export const useTypeGetVideo = (id?: number) => {
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'typeId',
|
||||
value: id,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: undefined,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'universeId',
|
||||
value: undefined,
|
||||
},
|
||||
], ['name']
|
||||
],
|
||||
['name']
|
||||
);
|
||||
}, [store.data, id]);
|
||||
return { isLoading: store.isLoading, videoWithType };
|
||||
};
|
||||
|
||||
export const useTypeSeriesGetVideo = (idType?: number, idSeries?: number) => {
|
||||
const { store } = useMediaService();
|
||||
const videoWithType = useMemo(() => {
|
||||
if (idType === undefined) {
|
||||
return [];
|
||||
}
|
||||
if (idSeries === undefined) {
|
||||
return [];
|
||||
}
|
||||
return DataTools.getsWhere(
|
||||
store.data,
|
||||
[
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'typeId',
|
||||
value: idType,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: idSeries,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seasonId',
|
||||
value: undefined,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'universeId',
|
||||
value: undefined,
|
||||
},
|
||||
],
|
||||
['name']
|
||||
);
|
||||
}, [store.data, idType]);
|
||||
return { isLoading: store.isLoading, videoWithType };
|
||||
};
|
||||
|
||||
export const useTypeSeriesSeasonGetVideo = (idType?: number, idSeries?: number, idSeason?: number) => {
|
||||
const { store } = useMediaService();
|
||||
const videoWithType = useMemo(() => {
|
||||
if (idType === undefined) {
|
||||
return [];
|
||||
}
|
||||
if (idSeries === undefined) {
|
||||
return [];
|
||||
}
|
||||
if (idSeason === undefined) {
|
||||
return [];
|
||||
}
|
||||
return DataTools.getsWhere(
|
||||
store.data,
|
||||
[
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'typeId',
|
||||
value: idType,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seriesId',
|
||||
value: idSeries,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'seasonId',
|
||||
value: idSeason,
|
||||
},
|
||||
{
|
||||
check: TypeCheck.EQUAL,
|
||||
key: 'universeId',
|
||||
value: undefined,
|
||||
},
|
||||
],
|
||||
['episode', 'name']
|
||||
);
|
||||
}, [store.data, idType]);
|
||||
return { isLoading: store.isLoading, videoWithType };
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ export namespace DataUrlAccess {
|
||||
});
|
||||
console.log(`get URL = ${url}`);
|
||||
if (environment?.replaceDataToRealServer === true) {
|
||||
return url.replace('http://localhost:19080', 'https://atria-soft.org');
|
||||
return url.replace('http://localhost:18080', 'https://atria-soft.org');
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@ -46,7 +46,7 @@ export namespace DataUrlAccess {
|
||||
},
|
||||
});
|
||||
if (environment?.replaceDataToRealServer === true) {
|
||||
return url.replace('http://localhost:19080', 'https://atria-soft.org');
|
||||
return url.replace('http://localhost:18080', 'https://atria-soft.org');
|
||||
}
|
||||
return url;
|
||||
};
|
||||
@ -82,7 +82,7 @@ export namespace DataUrlAccess {
|
||||
},
|
||||
});
|
||||
if (environment?.replaceDataToRealServer === true) {
|
||||
return url.replace('http://localhost:19080', 'https://atria-soft.org');
|
||||
return url.replace('http://localhost:18080', 'https://atria-soft.org');
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
@ -4,7 +4,23 @@ import { defineConfig } from 'vite';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
|
||||
plugins: [
|
||||
// other Vite plugins
|
||||
react({
|
||||
babel: {
|
||||
plugins: [
|
||||
// other Babel plugins
|
||||
[
|
||||
"@locator/babel-jsx/dist",
|
||||
{
|
||||
env: "development",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user