From f60c6763deef3eaec51d4c3e2515534b6f073dbf Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 16:08:14 +0200 Subject: [PATCH 01/14] OV-421: + add empty templates menu --- .../video-menu/components/menu-content/content.ts | 1 + .../templates-content/templates-content.tsx | 5 +++++ .../studio/components/video-menu/video-menu.tsx | 11 ++++++----- frontend/src/bundles/studio/enums/menu-items.enum.ts | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/content.ts b/frontend/src/bundles/studio/components/video-menu/components/menu-content/content.ts index fee93901e..ad57e9cdd 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/content.ts +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/content.ts @@ -1,3 +1,4 @@ export { AvatarsContent } from './avatars-content/avatars-content.js'; export { BackgroundsContent } from './backgrounds-content/backgrounds-content.js'; export { ScriptContent } from './script-content/script-content.js'; +export { TemplatesContent } from './templates-content/templates-content.js'; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx new file mode 100644 index 000000000..46f4520fa --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -0,0 +1,5 @@ +const TemplatesContent: React.FC = () => { + return
Templates
; +}; + +export { TemplatesContent }; diff --git a/frontend/src/bundles/studio/components/video-menu/video-menu.tsx b/frontend/src/bundles/studio/components/video-menu/video-menu.tsx index 652226737..36bc6505b 100644 --- a/frontend/src/bundles/studio/components/video-menu/video-menu.tsx +++ b/frontend/src/bundles/studio/components/video-menu/video-menu.tsx @@ -19,6 +19,7 @@ import { AvatarsContent, BackgroundsContent, ScriptContent, + TemplatesContent, } from './components/menu-content/content.js'; // import { // AssetsContent, @@ -66,11 +67,11 @@ const VideoMenu: React.FC = () => { // TODO: Uncomment menu items after demo const menuItems: Record, MenuItem> = { - // templates: { - // label: 'Templates', - // icon: , - // getContent: () => , - // }, + templates: { + label: 'Templates', + icon: , + getContent: () => , + }, avatars: { label: 'Avatars', icon: , diff --git a/frontend/src/bundles/studio/enums/menu-items.enum.ts b/frontend/src/bundles/studio/enums/menu-items.enum.ts index f64cda74d..ccbfe0e73 100644 --- a/frontend/src/bundles/studio/enums/menu-items.enum.ts +++ b/frontend/src/bundles/studio/enums/menu-items.enum.ts @@ -1,6 +1,6 @@ // TODO: Uncomment menu items after demo const MenuItems = { - // TEMPLATES: 'templates', + TEMPLATES: 'templates', AVATARS: 'avatars', SCRIPT: 'script', // TEXT: 'text', From 41595004d19a47902e9602c06805a3b4d21b188f Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 18:15:36 +0200 Subject: [PATCH 02/14] OV-421: * extract image card to reusable component --- .../backgrounds-content.tsx | 4 ++-- .../components/components.ts | 2 +- .../components/image-card.tsx | 19 ++++----------- .../components/styles.module.css | 15 ------------ .../menu-content/components/components.ts | 1 + .../components/image-card/image-card.tsx | 23 +++++++++++++++++++ .../components/image-card/styles.module.css | 14 +++++++++++ 7 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/components/components.ts create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/styles.module.css diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/backgrounds-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/backgrounds-content.tsx index b0cc6af44..c780726e1 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/backgrounds-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/backgrounds-content.tsx @@ -14,7 +14,7 @@ import backgroundColors from '~/bundles/studio/data/bg-colors.json'; import backgroundImages from '~/bundles/studio/data/bg-images.json'; import { actions as studioActions } from '~/bundles/studio/store/studio.js'; -import { ColorCard, ImageCard } from './components/components.js'; +import { BackgroundImageCard, ColorCard } from './components/components.js'; import styles from './styles.module.css'; const BackgroundsContent: React.FC = () => { @@ -43,7 +43,7 @@ const BackgroundsContent: React.FC = () => { {backgroundImages.map((imageSource, index) => ( - diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/components.ts b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/components.ts index f699b1c2e..f4a76ce49 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/components.ts +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/components.ts @@ -1,2 +1,2 @@ export { ColorCard } from './color-card.js'; -export { ImageCard } from './image-card.js'; +export { BackgroundImageCard } from './image-card.js'; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/image-card.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/image-card.tsx index bc8348e52..9bf020cd8 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/image-card.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/image-card.tsx @@ -1,14 +1,12 @@ -import { Box, Image } from '~/bundles/common/components/components.js'; import { useAppDispatch, useCallback } from '~/bundles/common/hooks/hooks.js'; +import { ImageCard } from '~/bundles/studio/components/video-menu/components/menu-content/components/components.js'; import { actions as studioActions } from '~/bundles/studio/store/studio.js'; -import styles from './styles.module.css'; - type Properties = { imageSource: string; }; -const ImageCard: React.FC = ({ imageSource }) => { +const BackgroundImageCard: React.FC = ({ imageSource }) => { const dispatch = useAppDispatch(); const handleImageClick = useCallback((): void => { @@ -20,16 +18,7 @@ const ImageCard: React.FC = ({ imageSource }) => { ); }, [dispatch, imageSource]); - return ( - - - - ); + return ; }; -export { ImageCard }; +export { BackgroundImageCard }; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/styles.module.css b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/styles.module.css index add5f9567..fe90fe7cf 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/styles.module.css +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/backgrounds-content/components/styles.module.css @@ -1,12 +1,3 @@ -.image-item { - height: 100px; - background-color: var(--chakra-colors-background-700); - border-radius: 7px; - border-width: 1px; - border-color: transparent; - transition: all 0.3s ease; -} - .color-item { border-radius: 7px; height: 80px; @@ -15,12 +6,6 @@ transition: all 0.3s ease; } -.image-item:hover { - background-color: var(--chakra-colors-background-600); - border-color: var(--chakra-colors-brand-secondary-300); - cursor: pointer; -} - .color-item:hover { border-color: var(--chakra-colors-brand-secondary-300); cursor: pointer; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/components.ts b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/components.ts new file mode 100644 index 000000000..53d3191ca --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/components.ts @@ -0,0 +1 @@ +export { ImageCard } from './image-card/image-card.js'; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx new file mode 100644 index 000000000..bab941c87 --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx @@ -0,0 +1,23 @@ +import { Box, Image } from '~/bundles/common/components/components.js'; + +import styles from './styles.module.css'; + +type Properties = { + imageSource: string; + onClick: () => void; +}; + +const ImageCard: React.FC = ({ imageSource, onClick }) => { + return ( + + + + ); +}; + +export { ImageCard }; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/styles.module.css b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/styles.module.css new file mode 100644 index 000000000..e6407a873 --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/styles.module.css @@ -0,0 +1,14 @@ +.image-item { + height: 100px; + background-color: var(--chakra-colors-background-700); + border-radius: 7px; + border-width: 1px; + border-color: transparent; + transition: all 0.3s ease; +} + +.image-item:hover { + background-color: var(--chakra-colors-background-600); + border-color: var(--chakra-colors-brand-secondary-300); + cursor: pointer; +} From 000e7d3a57f7dad65b3e4d968373bbc58c5c8ee4 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 18:36:52 +0200 Subject: [PATCH 03/14] OV-421: * templates menu layout --- .../components/image-card/image-card.tsx | 2 +- .../components/components.ts | 1 + .../components/template-card.tsx | 11 ++++++ .../templates-content/templates-content.tsx | 37 ++++++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/components.ts create mode 100644 frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx index bab941c87..1960c610a 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/components/image-card/image-card.tsx @@ -4,7 +4,7 @@ import styles from './styles.module.css'; type Properties = { imageSource: string; - onClick: () => void; + onClick?: () => void; }; const ImageCard: React.FC = ({ imageSource, onClick }) => { diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/components.ts b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/components.ts new file mode 100644 index 000000000..a302af5fc --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/components.ts @@ -0,0 +1 @@ +export { TemplateCard } from './template-card.js'; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx new file mode 100644 index 000000000..54450f275 --- /dev/null +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx @@ -0,0 +1,11 @@ +import { ImageCard } from '~/bundles/studio/components/video-menu/components/menu-content/components/components.js'; + +type Properties = { + imageSource: string; +}; + +const TemplateCard: React.FC = ({ imageSource }) => { + return ; +}; + +export { TemplateCard }; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx index 46f4520fa..586521603 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -1,5 +1,40 @@ +import { + SimpleGrid, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + Text, +} from '~/bundles/common/components/components.js'; + +import { TemplateCard } from './components/components.js'; + const TemplatesContent: React.FC = () => { - return
Templates
; + return ( + + + Templates + My templates + + + + + + + + + + + + + + You have no templates yet. + + + + + ); }; export { TemplatesContent }; From 5637a2ff49fa323598cd94a384fb4ff9301acb6c Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 19:00:46 +0200 Subject: [PATCH 04/14] OV-421: + template api path enum in shared --- backend/src/bundles/templates/enums/enums.ts | 2 +- .../src/bundles/templates/templates.controller.ts | 14 +++++++------- frontend/src/bundles/studio/types/types.ts | 1 + shared/src/bundles/templates/enums/enums.ts | 1 + .../templates/enums/templates-api-path.enum.ts | 4 ++-- shared/src/bundles/templates/templates.ts | 5 ++++- shared/src/index.ts | 1 + 7 files changed, 17 insertions(+), 11 deletions(-) rename {backend => shared}/src/bundles/templates/enums/templates-api-path.enum.ts (61%) diff --git a/backend/src/bundles/templates/enums/enums.ts b/backend/src/bundles/templates/enums/enums.ts index f85dd91b5..161821e35 100644 --- a/backend/src/bundles/templates/enums/enums.ts +++ b/backend/src/bundles/templates/enums/enums.ts @@ -1,2 +1,2 @@ export { templateErrorMessage } from './template-error-message.enum.js'; -export { templateApiPath } from './templates-api-path.enum.js'; +export { TemplateApiPath } from 'shared'; diff --git a/backend/src/bundles/templates/templates.controller.ts b/backend/src/bundles/templates/templates.controller.ts index c52672322..0d539a5f2 100644 --- a/backend/src/bundles/templates/templates.controller.ts +++ b/backend/src/bundles/templates/templates.controller.ts @@ -7,7 +7,7 @@ import { ApiPath } from '~/common/enums/enums.js'; import { HTTPCode, HTTPMethod } from '~/common/http/http.js'; import { type Logger } from '~/common/logger/logger.js'; -import { templateApiPath } from './enums/enums.js'; +import { TemplateApiPath } from './enums/enums.js'; import { type TemplateService } from './templates.service.js'; import { type CreateTemplateRequestDto, @@ -111,7 +111,7 @@ class TemplateController extends BaseController { this.templateService = templateService; this.addRoute({ - path: templateApiPath.USER, + path: TemplateApiPath.USER, method: HTTPMethod.GET, handler: (options) => this.findAllByUser( @@ -122,7 +122,7 @@ class TemplateController extends BaseController { }); this.addRoute({ - path: templateApiPath.PUBLIC, + path: TemplateApiPath.PUBLIC, method: HTTPMethod.GET, handler: () => { return this.findPublicTemplates(); @@ -130,7 +130,7 @@ class TemplateController extends BaseController { }); this.addRoute({ - path: templateApiPath.ID, + path: TemplateApiPath.ID, method: HTTPMethod.GET, handler: (options) => { return this.findById( @@ -142,7 +142,7 @@ class TemplateController extends BaseController { }); this.addRoute({ - path: templateApiPath.ROOT, + path: TemplateApiPath.ROOT, method: HTTPMethod.POST, validation: { body: createTemplateValidationSchema, @@ -156,7 +156,7 @@ class TemplateController extends BaseController { }); this.addRoute({ - path: templateApiPath.ID, + path: TemplateApiPath.ID, method: HTTPMethod.PATCH, validation: { body: updateTemplateValidationSchema, @@ -172,7 +172,7 @@ class TemplateController extends BaseController { }); this.addRoute({ - path: templateApiPath.ID, + path: TemplateApiPath.ID, method: HTTPMethod.DELETE, handler: (options) => this.delete( diff --git a/frontend/src/bundles/studio/types/types.ts b/frontend/src/bundles/studio/types/types.ts index d9342d973..b17ba9be0 100644 --- a/frontend/src/bundles/studio/types/types.ts +++ b/frontend/src/bundles/studio/types/types.ts @@ -16,6 +16,7 @@ export { type CreateVideoRequestDto, type GenerateSpeechRequestDto, type GenerateSpeechResponseDto, + type GetTemplatesResponseDto, type GetVoicesResponseDto, type RenderAvatarResponseDto, type RenderAvatarVideoRequestDto, diff --git a/shared/src/bundles/templates/enums/enums.ts b/shared/src/bundles/templates/enums/enums.ts index 3aecae376..79e9ce080 100644 --- a/shared/src/bundles/templates/enums/enums.ts +++ b/shared/src/bundles/templates/enums/enums.ts @@ -1 +1,2 @@ export { TemplateValidationErrorMessage } from './template-validation-error-message.enum.js'; +export { TemplateApiPath } from './templates-api-path.enum.js'; diff --git a/backend/src/bundles/templates/enums/templates-api-path.enum.ts b/shared/src/bundles/templates/enums/templates-api-path.enum.ts similarity index 61% rename from backend/src/bundles/templates/enums/templates-api-path.enum.ts rename to shared/src/bundles/templates/enums/templates-api-path.enum.ts index 7b12bd25a..f3af6a0de 100644 --- a/backend/src/bundles/templates/enums/templates-api-path.enum.ts +++ b/shared/src/bundles/templates/enums/templates-api-path.enum.ts @@ -1,8 +1,8 @@ -const templateApiPath = { +const TemplateApiPath = { ROOT: '/', ID: '/:id', PUBLIC: '/public', USER: '/user', } as const; -export { templateApiPath }; +export { TemplateApiPath }; diff --git a/shared/src/bundles/templates/templates.ts b/shared/src/bundles/templates/templates.ts index 8e2c4700e..65fd81cad 100644 --- a/shared/src/bundles/templates/templates.ts +++ b/shared/src/bundles/templates/templates.ts @@ -1,4 +1,7 @@ -export { TemplateValidationErrorMessage } from './enums/enums.js'; +export { + TemplateApiPath, + TemplateValidationErrorMessage, +} from './enums/enums.js'; export { type CreateTemplateRequestDto, type CreateTemplateResponseDto, diff --git a/shared/src/index.ts b/shared/src/index.ts index 85b9a1b3b..bca191dd1 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -57,6 +57,7 @@ export { type Template, type UpdateTemplateRequestDto, createTemplateValidationSchema, + TemplateApiPath, TemplateValidationErrorMessage, updateTemplateValidationSchema, } from './bundles/templates/templates.js'; From 6218b95ea8f429bb2325169f709670dc2b2e3673 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 19:07:30 +0200 Subject: [PATCH 05/14] OV-421: + templates api --- frontend/src/bundles/studio/enums/enums.ts | 2 +- frontend/src/bundles/studio/studio.ts | 9 ++++- frontend/src/bundles/studio/templates-api.ts | 34 +++++++++++++++++++ frontend/src/framework/store/store.package.ts | 3 ++ 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 frontend/src/bundles/studio/templates-api.ts diff --git a/frontend/src/bundles/studio/enums/enums.ts b/frontend/src/bundles/studio/enums/enums.ts index 7787da3d4..f2fc60aff 100644 --- a/frontend/src/bundles/studio/enums/enums.ts +++ b/frontend/src/bundles/studio/enums/enums.ts @@ -3,4 +3,4 @@ export { NotificationMessage } from './notification-message.enum.js'; export { NotificationTitle } from './notification-title.enum.js'; export { PlayIconNames } from './play-icon-names.enum.js'; export { RowNames } from './row-names.enum.js'; -export { AvatarsApiPath, SpeechApiPath } from 'shared'; +export { AvatarsApiPath, SpeechApiPath, TemplateApiPath } from 'shared'; diff --git a/frontend/src/bundles/studio/studio.ts b/frontend/src/bundles/studio/studio.ts index cce31381f..67b15511c 100644 --- a/frontend/src/bundles/studio/studio.ts +++ b/frontend/src/bundles/studio/studio.ts @@ -5,6 +5,7 @@ import { storage } from '~/framework/storage/storage.js'; import { AvatarVideosApi } from './avatar-videos-api.js'; import { AvatarsApi } from './avatars-api.js'; import { SpeechApi } from './speech-api.js'; +import { TemplatesApi } from './templates-api.js'; const avatarVideosApi = new AvatarVideosApi({ baseUrl: config.ENV.API.ORIGIN_URL, @@ -24,4 +25,10 @@ const speechApi = new SpeechApi({ http, }); -export { avatarsApi, avatarVideosApi, speechApi }; +const templatesApi = new TemplatesApi({ + baseUrl: config.ENV.API.ORIGIN_URL, + storage, + http, +}); + +export { avatarsApi, avatarVideosApi, speechApi, templatesApi }; diff --git a/frontend/src/bundles/studio/templates-api.ts b/frontend/src/bundles/studio/templates-api.ts new file mode 100644 index 000000000..bb7dbf8ac --- /dev/null +++ b/frontend/src/bundles/studio/templates-api.ts @@ -0,0 +1,34 @@ +import { ApiPath, ContentType } from '~/bundles/common/enums/enums.js'; +import { type Http, HTTPMethod } from '~/framework/http/http.js'; +import { BaseHttpApi } from '~/framework/http-api/http-api.js'; +import { type Storage } from '~/framework/storage/storage.js'; + +import { TemplateApiPath } from './enums/enums.js'; +import { type GetTemplatesResponseDto } from './types/types.js'; + +type Constructor = { + baseUrl: string; + http: Http; + storage: Storage; +}; + +class TemplatesApi extends BaseHttpApi { + public constructor({ baseUrl, http, storage }: Constructor) { + super({ path: ApiPath.TEMPLATES, baseUrl, http, storage }); + } + + public async loadPublicTemplates(): Promise { + const response = await this.load( + this.getFullEndpoint(TemplateApiPath.PUBLIC, {}), + { + method: HTTPMethod.GET, + contentType: ContentType.JSON, + hasAuth: true, + }, + ); + + return response.json(); + } +} + +export { TemplatesApi }; diff --git a/frontend/src/framework/store/store.package.ts b/frontend/src/framework/store/store.package.ts index 33e2f8f29..8b8ef4504 100644 --- a/frontend/src/framework/store/store.package.ts +++ b/frontend/src/framework/store/store.package.ts @@ -21,6 +21,7 @@ import { avatarsApi, avatarVideosApi, speechApi, + templatesApi, } from '~/bundles/studio/studio.js'; import { userApi } from '~/bundles/users/users.js'; import { type Config } from '~/framework/config/config.js'; @@ -41,6 +42,7 @@ type ExtraArguments = { speechApi: typeof speechApi; avatarVideosApi: typeof avatarVideosApi; chatApi: typeof chatApi; + templatesApi: typeof templatesApi; storage: typeof storage; }; @@ -86,6 +88,7 @@ class Store { speechApi, avatarVideosApi, chatApi, + templatesApi, storage, }; } From e61bf568e66d38c86936ede0799239c2bbb64a81 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 20:53:03 +0200 Subject: [PATCH 06/14] OV-421: * add action and reducers for loading public templates --- frontend/src/bundles/studio/store/actions.ts | 12 +++++++++++ frontend/src/bundles/studio/store/slice.ts | 21 ++++++++++++++++++++ frontend/src/bundles/studio/store/studio.ts | 2 ++ frontend/src/bundles/studio/types/types.ts | 1 + 4 files changed, 36 insertions(+) diff --git a/frontend/src/bundles/studio/store/actions.ts b/frontend/src/bundles/studio/store/actions.ts index 05c79464b..b619b0e8f 100644 --- a/frontend/src/bundles/studio/store/actions.ts +++ b/frontend/src/bundles/studio/store/actions.ts @@ -8,6 +8,7 @@ import { type CreateVideoRequestDto, type GenerateSpeechRequestDto, type GenerateSpeechResponseDto, + type GetTemplatesResponseDto, type GetVoicesResponseDto, type RenderAvatarResponseDto, type Script, @@ -131,11 +132,22 @@ const updateVideo = createAsyncThunk< return videosApi.updateVideo(payload, videoId as string); }); +const loadPublicTemplates = createAsyncThunk< + GetTemplatesResponseDto, + undefined, + AsyncThunkConfig +>(`${sliceName}/load-public-templates`, (_, { extra }) => { + const { templatesApi } = extra; + + return templatesApi.loadPublicTemplates(); +}); + export { generateAllScriptsSpeech, generateScriptSpeech, generateScriptSpeechPreview, loadAvatars, + loadPublicTemplates, loadVoices, renderAvatar, saveVideo, diff --git a/frontend/src/bundles/studio/store/slice.ts b/frontend/src/bundles/studio/store/slice.ts index d7ac0993a..800ce88d0 100644 --- a/frontend/src/bundles/studio/store/slice.ts +++ b/frontend/src/bundles/studio/store/slice.ts @@ -31,6 +31,7 @@ import { type RowType, type Scene, type SceneAvatar, + type Template, type TimelineItemWithSpan, type VideoGetAllItemResponseDto, type Voice, @@ -40,6 +41,7 @@ import { generateScriptSpeech, generateScriptSpeechPreview, loadAvatars, + loadPublicTemplates, loadVoices, renderAvatar, saveVideo, @@ -87,6 +89,10 @@ type State = { isDraftSaved: boolean; videoId: string | null; voices: Voice[]; + templates: { + public: Template[] | []; + user: Template[] | []; + }; ui: { destinationPointer: DestinationPointer | null; selectedItem: SelectedItem | null; @@ -111,6 +117,10 @@ const initialState: State = { isDraftSaved: true, videoId: null, voices: [], + templates: { + public: [], + user: [], + }, ui: { destinationPointer: null, selectedItem: null, @@ -519,6 +529,17 @@ const { reducer, actions, name } = createSlice({ state.dataStatus = DataStatus.REJECTED; state.isDraftSaved = false; }); + builder.addCase(loadPublicTemplates.pending, (state) => { + state.dataStatus = DataStatus.PENDING; + }); + builder.addCase(loadPublicTemplates.fulfilled, (state, action) => { + state.dataStatus = DataStatus.FULFILLED; + state.templates.public = action.payload.items; + }); + builder.addCase(loadPublicTemplates.rejected, (state) => { + state.dataStatus = DataStatus.REJECTED; + state.templates.public = []; + }); }, }); diff --git a/frontend/src/bundles/studio/store/studio.ts b/frontend/src/bundles/studio/store/studio.ts index 1ed89e8bc..6f664ea64 100644 --- a/frontend/src/bundles/studio/store/studio.ts +++ b/frontend/src/bundles/studio/store/studio.ts @@ -3,6 +3,7 @@ import { generateScriptSpeech, generateScriptSpeechPreview, loadAvatars, + loadPublicTemplates, loadVoices, renderAvatar, saveVideo, @@ -20,6 +21,7 @@ const allActions = { renderAvatar, saveVideo, updateVideo, + loadPublicTemplates, }; export { allActions as actions }; diff --git a/frontend/src/bundles/studio/types/types.ts b/frontend/src/bundles/studio/types/types.ts index b17ba9be0..7fb779215 100644 --- a/frontend/src/bundles/studio/types/types.ts +++ b/frontend/src/bundles/studio/types/types.ts @@ -22,6 +22,7 @@ export { type RenderAvatarVideoRequestDto, type Scene, type SceneAvatar, + type Template, type VideoGetAllItemResponseDto, type Voice, } from 'shared'; From 86ad5c991810f4c588240a874afc5d495613e9ce Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 20:54:32 +0200 Subject: [PATCH 07/14] OV-421: * display public templates in menu --- .../templates-content/templates-content.tsx | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx index 586521603..214ccd34b 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -1,4 +1,6 @@ import { + Box, + Loader, SimpleGrid, Tab, TabList, @@ -7,32 +9,58 @@ import { Tabs, Text, } from '~/bundles/common/components/components.js'; +import { EMPTY_VALUE } from '~/bundles/common/constants/constants.js'; +import { DataStatus } from '~/bundles/common/enums/data-status.enum.js'; +import { + useAppDispatch, + useAppSelector, + useEffect, +} from '~/bundles/common/hooks/hooks.js'; +import { actions as studioActions } from '~/bundles/studio/store/studio.js'; import { TemplateCard } from './components/components.js'; const TemplatesContent: React.FC = () => { + const dispatch = useAppDispatch(); + + const { templates, dataStatus } = useAppSelector(({ studio }) => studio); + + useEffect(() => { + if (templates.public.length === EMPTY_VALUE) { + void dispatch(studioActions.loadPublicTemplates()); + } + }, [dispatch, templates]); + return ( Templates My templates - - - - - - - - - - - - - You have no templates yet. - - - + + {dataStatus === DataStatus.PENDING ? ( + + + + ) : ( + + + + {templates.public.map((template) => ( + + ))} + + + + + You have no templates yet. + + + + )} ); }; From 922e64db733b47b0a39ea0055cbaa9a9798fb5b5 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 21:42:04 +0200 Subject: [PATCH 08/14] OV-421: * load and display user templates --- .../templates-content/templates-content.tsx | 18 +++++++++++++++--- frontend/src/bundles/studio/store/actions.ts | 11 +++++++++++ frontend/src/bundles/studio/store/slice.ts | 16 ++++++++++++++++ frontend/src/bundles/studio/store/studio.ts | 2 ++ frontend/src/bundles/studio/templates-api.ts | 13 +++++++++++++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx index 214ccd34b..c2e3dbd41 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -29,6 +29,9 @@ const TemplatesContent: React.FC = () => { if (templates.public.length === EMPTY_VALUE) { void dispatch(studioActions.loadPublicTemplates()); } + if (!templates.isUserLoaded) { + void dispatch(studioActions.loadUserTemplates()); + } }, [dispatch, templates]); return ( @@ -55,9 +58,18 @@ const TemplatesContent: React.FC = () => { - - You have no templates yet. - + {templates.user.length > EMPTY_VALUE ? ( + templates.user.map((template) => ( + + )) + ) : ( + + You have no templates yet. + + )} )} diff --git a/frontend/src/bundles/studio/store/actions.ts b/frontend/src/bundles/studio/store/actions.ts index b619b0e8f..e3425fd75 100644 --- a/frontend/src/bundles/studio/store/actions.ts +++ b/frontend/src/bundles/studio/store/actions.ts @@ -142,12 +142,23 @@ const loadPublicTemplates = createAsyncThunk< return templatesApi.loadPublicTemplates(); }); +const loadUserTemplates = createAsyncThunk< + GetTemplatesResponseDto, + undefined, + AsyncThunkConfig +>(`${sliceName}/load-user-templates`, (_, { extra }) => { + const { templatesApi } = extra; + + return templatesApi.loadUserTemplates(); +}); + export { generateAllScriptsSpeech, generateScriptSpeech, generateScriptSpeechPreview, loadAvatars, loadPublicTemplates, + loadUserTemplates, loadVoices, renderAvatar, saveVideo, diff --git a/frontend/src/bundles/studio/store/slice.ts b/frontend/src/bundles/studio/store/slice.ts index 800ce88d0..f6b0b4385 100644 --- a/frontend/src/bundles/studio/store/slice.ts +++ b/frontend/src/bundles/studio/store/slice.ts @@ -42,6 +42,7 @@ import { generateScriptSpeechPreview, loadAvatars, loadPublicTemplates, + loadUserTemplates, loadVoices, renderAvatar, saveVideo, @@ -92,6 +93,7 @@ type State = { templates: { public: Template[] | []; user: Template[] | []; + isUserLoaded: boolean; }; ui: { destinationPointer: DestinationPointer | null; @@ -120,6 +122,7 @@ const initialState: State = { templates: { public: [], user: [], + isUserLoaded: false, }, ui: { destinationPointer: null, @@ -540,6 +543,19 @@ const { reducer, actions, name } = createSlice({ state.dataStatus = DataStatus.REJECTED; state.templates.public = []; }); + builder.addCase(loadUserTemplates.pending, (state) => { + state.dataStatus = DataStatus.PENDING; + }); + builder.addCase(loadUserTemplates.fulfilled, (state, action) => { + state.dataStatus = DataStatus.FULFILLED; + state.templates.user = action.payload.items; + state.templates.isUserLoaded = true; + }); + builder.addCase(loadUserTemplates.rejected, (state) => { + state.dataStatus = DataStatus.REJECTED; + state.templates.user = []; + state.templates.isUserLoaded = false; + }); }, }); diff --git a/frontend/src/bundles/studio/store/studio.ts b/frontend/src/bundles/studio/store/studio.ts index 6f664ea64..f7bb9439b 100644 --- a/frontend/src/bundles/studio/store/studio.ts +++ b/frontend/src/bundles/studio/store/studio.ts @@ -4,6 +4,7 @@ import { generateScriptSpeechPreview, loadAvatars, loadPublicTemplates, + loadUserTemplates, loadVoices, renderAvatar, saveVideo, @@ -22,6 +23,7 @@ const allActions = { saveVideo, updateVideo, loadPublicTemplates, + loadUserTemplates, }; export { allActions as actions }; diff --git a/frontend/src/bundles/studio/templates-api.ts b/frontend/src/bundles/studio/templates-api.ts index bb7dbf8ac..bab2a109e 100644 --- a/frontend/src/bundles/studio/templates-api.ts +++ b/frontend/src/bundles/studio/templates-api.ts @@ -29,6 +29,19 @@ class TemplatesApi extends BaseHttpApi { return response.json(); } + + public async loadUserTemplates(): Promise { + const response = await this.load( + this.getFullEndpoint(TemplateApiPath.USER, {}), + { + method: HTTPMethod.GET, + contentType: ContentType.JSON, + hasAuth: true, + }, + ); + + return response.json(); + } } export { TemplatesApi }; From 2c344c18bdd43827ef9a0c9334192b77c4980e13 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Thu, 26 Sep 2024 22:35:28 +0200 Subject: [PATCH 09/14] OV-421: * add functionality to create template --- .../templates-content/templates-content.tsx | 18 ++++++++++----- .../src/bundles/studio/constants/constants.ts | 2 ++ .../template-save-failed-notification-id.ts | 3 +++ .../template-save-notification-id.ts | 3 +++ .../studio/enums/notification-message.enum.ts | 2 ++ .../studio/enums/notification-title.enum.ts | 2 ++ frontend/src/bundles/studio/pages/studio.tsx | 23 +++++++++++++++++++ frontend/src/bundles/studio/store/actions.ts | 20 ++++++++++++++++ frontend/src/bundles/studio/store/slice.ts | 11 +++++++++ frontend/src/bundles/studio/store/studio.ts | 2 ++ frontend/src/bundles/studio/templates-api.ts | 22 +++++++++++++++++- frontend/src/bundles/studio/types/types.ts | 2 ++ 12 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 frontend/src/bundles/studio/constants/template-save-failed-notification-id.ts create mode 100644 frontend/src/bundles/studio/constants/template-save-notification-id.ts diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx index c2e3dbd41..bead19002 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -59,12 +59,18 @@ const TemplatesContent: React.FC = () => { {templates.user.length > EMPTY_VALUE ? ( - templates.user.map((template) => ( - - )) + + {templates.user.map((template) => ( + + ))} + ) : ( You have no templates yet. diff --git a/frontend/src/bundles/studio/constants/constants.ts b/frontend/src/bundles/studio/constants/constants.ts index 31bc043be..e673e7175 100644 --- a/frontend/src/bundles/studio/constants/constants.ts +++ b/frontend/src/bundles/studio/constants/constants.ts @@ -10,6 +10,8 @@ export { MIN_SCENE_DURATION } from './min-scene-duration.constant.js'; export { NEW_SCRIPT_TEXT } from './new-script-text.constant.js'; export { SCRIPT_AND_AVATAR_ARE_REQUIRED } from './script-and-avatar-are-required.constant.js'; export { SKIP_TO_PREV_SCENE_THRESHOLD } from './skip-to-previous-scene-threshold.constant.js'; +export { TEMPLATE_SAVE_FAILED_NOTOFICATION_ID } from './template-save-failed-notification-id.js'; +export { TEMPLATE_SAVE_NOTOFICATION_ID } from './template-save-notification-id.js'; export { VIDEO_SAVE_FAILED_NOTIFICATION_ID } from './video-save-failed-notification-id.js'; export { VIDEO_SAVE_NOTIFICATION_ID } from './video-save-notification-id.js'; export { VIDEO_SUBMIT_FAILED_NOTIFICATION_ID } from './video-submit-failed-id.constant.js'; diff --git a/frontend/src/bundles/studio/constants/template-save-failed-notification-id.ts b/frontend/src/bundles/studio/constants/template-save-failed-notification-id.ts new file mode 100644 index 000000000..c0549068e --- /dev/null +++ b/frontend/src/bundles/studio/constants/template-save-failed-notification-id.ts @@ -0,0 +1,3 @@ +const TEMPLATE_SAVE_FAILED_NOTOFICATION_ID = 'template-save-failed'; + +export { TEMPLATE_SAVE_FAILED_NOTOFICATION_ID }; diff --git a/frontend/src/bundles/studio/constants/template-save-notification-id.ts b/frontend/src/bundles/studio/constants/template-save-notification-id.ts new file mode 100644 index 000000000..3b3bef74a --- /dev/null +++ b/frontend/src/bundles/studio/constants/template-save-notification-id.ts @@ -0,0 +1,3 @@ +const TEMPLATE_SAVE_NOTOFICATION_ID = 'template-save'; + +export { TEMPLATE_SAVE_NOTOFICATION_ID }; diff --git a/frontend/src/bundles/studio/enums/notification-message.enum.ts b/frontend/src/bundles/studio/enums/notification-message.enum.ts index ce2c28620..78c9518e9 100644 --- a/frontend/src/bundles/studio/enums/notification-message.enum.ts +++ b/frontend/src/bundles/studio/enums/notification-message.enum.ts @@ -5,6 +5,8 @@ const NotificationMessage = { 'To create a video, you need to have an avatar and a script.', VIDEO_SAVE: 'Video saved successfully', VIDEO_SAVE_FAILED: 'Video save failed', + TEMPLATE_SAVE: 'Template saved successfully', + TEMPLATE_SAVE_FAILED: 'Template save failed', } as const; export { NotificationMessage }; diff --git a/frontend/src/bundles/studio/enums/notification-title.enum.ts b/frontend/src/bundles/studio/enums/notification-title.enum.ts index cf73f0ea2..0e7b5f6cb 100644 --- a/frontend/src/bundles/studio/enums/notification-title.enum.ts +++ b/frontend/src/bundles/studio/enums/notification-title.enum.ts @@ -4,6 +4,8 @@ const NotificationTitle = { SCRIPT_AND_AVATAR_ARE_REQUIRED: 'Script and avatar are required', VIDEO_SAVED: 'Video saved', VIDEO_SAVE_FAILED: 'Video save failed', + TEMPLATE_SAVED: 'Template saved', + TEMPLATE_SAVE_FAILED: 'Template save failed', } as const; export { NotificationTitle }; diff --git a/frontend/src/bundles/studio/pages/studio.tsx b/frontend/src/bundles/studio/pages/studio.tsx index 2ae012f91..d6211a0db 100644 --- a/frontend/src/bundles/studio/pages/studio.tsx +++ b/frontend/src/bundles/studio/pages/studio.tsx @@ -38,6 +38,8 @@ import { } from '../components/components.js'; import { SCRIPT_AND_AVATAR_ARE_REQUIRED, + TEMPLATE_SAVE_FAILED_NOTOFICATION_ID, + TEMPLATE_SAVE_NOTOFICATION_ID, VIDEO_SAVE_FAILED_NOTIFICATION_ID, VIDEO_SAVE_NOTIFICATION_ID, VIDEO_SUBMIT_FAILED_NOTIFICATION_ID, @@ -181,6 +183,24 @@ const Studio: React.FC = () => { [dispatch], ); + const handleSaveTemplate = useCallback((): void => { + void dispatch(studioActions.createTemplate()) + .then(() => { + notificationService.success({ + id: TEMPLATE_SAVE_NOTOFICATION_ID, + message: NotificationMessage.TEMPLATE_SAVE, + title: NotificationTitle.TEMPLATE_SAVED, + }); + }) + .catch(() => { + notificationService.error({ + id: TEMPLATE_SAVE_FAILED_NOTOFICATION_ID, + message: NotificationMessage.TEMPLATE_SAVE_FAILED, + title: NotificationTitle.TEMPLATE_SAVE_FAILED, + }); + }); + }, [dispatch]); + const { isPlaying, url } = scriptPlayer; return ( @@ -224,6 +244,9 @@ const Studio: React.FC = () => { Submit to render + + Save as template + diff --git a/frontend/src/bundles/studio/store/actions.ts b/frontend/src/bundles/studio/store/actions.ts index e3425fd75..9c9559a60 100644 --- a/frontend/src/bundles/studio/store/actions.ts +++ b/frontend/src/bundles/studio/store/actions.ts @@ -5,6 +5,7 @@ import { type UpdateVideoRequestDto } from 'shared'; import { type AsyncThunkConfig } from '~/bundles/common/types/types.js'; import { type AvatarGetAllResponseDto, + type CreateTemplateResponseDto, type CreateVideoRequestDto, type GenerateSpeechRequestDto, type GenerateSpeechResponseDto, @@ -152,7 +153,26 @@ const loadUserTemplates = createAsyncThunk< return templatesApi.loadUserTemplates(); }); +const createTemplate = createAsyncThunk< + CreateTemplateResponseDto, + undefined, + AsyncThunkConfig +>(`${sliceName}/create-template`, (_, { extra, getState }) => { + const { templatesApi } = extra; + const { scripts, scenes, videoName, videoSize } = getState().studio; + + return templatesApi.createTemplate({ + composition: { + scenes, + scripts: getVoicesConfigs(scripts), + videoOrientation: videoSize, + }, + name: videoName, + }); +}); + export { + createTemplate, generateAllScriptsSpeech, generateScriptSpeech, generateScriptSpeechPreview, diff --git a/frontend/src/bundles/studio/store/slice.ts b/frontend/src/bundles/studio/store/slice.ts index f6b0b4385..1f0633191 100644 --- a/frontend/src/bundles/studio/store/slice.ts +++ b/frontend/src/bundles/studio/store/slice.ts @@ -37,6 +37,7 @@ import { type Voice, } from '../types/types.js'; import { + createTemplate, generateAllScriptsSpeech, generateScriptSpeech, generateScriptSpeechPreview, @@ -556,6 +557,16 @@ const { reducer, actions, name } = createSlice({ state.templates.user = []; state.templates.isUserLoaded = false; }); + builder.addCase(createTemplate.pending, (state) => { + state.dataStatus = DataStatus.PENDING; + }); + builder.addCase(createTemplate.fulfilled, (state, action) => { + state.dataStatus = DataStatus.FULFILLED; + state.templates.user = [...state.templates.user, action.payload]; + }); + builder.addCase(createTemplate.rejected, (state) => { + state.dataStatus = DataStatus.REJECTED; + }); }, }); diff --git a/frontend/src/bundles/studio/store/studio.ts b/frontend/src/bundles/studio/store/studio.ts index f7bb9439b..e8244baed 100644 --- a/frontend/src/bundles/studio/store/studio.ts +++ b/frontend/src/bundles/studio/store/studio.ts @@ -1,4 +1,5 @@ import { + createTemplate, generateAllScriptsSpeech, generateScriptSpeech, generateScriptSpeechPreview, @@ -24,6 +25,7 @@ const allActions = { updateVideo, loadPublicTemplates, loadUserTemplates, + createTemplate, }; export { allActions as actions }; diff --git a/frontend/src/bundles/studio/templates-api.ts b/frontend/src/bundles/studio/templates-api.ts index bab2a109e..49c6babd7 100644 --- a/frontend/src/bundles/studio/templates-api.ts +++ b/frontend/src/bundles/studio/templates-api.ts @@ -4,7 +4,11 @@ import { BaseHttpApi } from '~/framework/http-api/http-api.js'; import { type Storage } from '~/framework/storage/storage.js'; import { TemplateApiPath } from './enums/enums.js'; -import { type GetTemplatesResponseDto } from './types/types.js'; +import { + type CreateTemplateRequestDto, + type CreateTemplateResponseDto, + type GetTemplatesResponseDto, +} from './types/types.js'; type Constructor = { baseUrl: string; @@ -42,6 +46,22 @@ class TemplatesApi extends BaseHttpApi { return response.json(); } + + public async createTemplate( + payload: CreateTemplateRequestDto, + ): Promise { + const response = await this.load( + this.getFullEndpoint(TemplateApiPath.ROOT, {}), + { + method: HTTPMethod.POST, + contentType: ContentType.JSON, + payload: JSON.stringify(payload), + hasAuth: true, + }, + ); + + return response.json(); + } } export { TemplatesApi }; diff --git a/frontend/src/bundles/studio/types/types.ts b/frontend/src/bundles/studio/types/types.ts index 7fb779215..e2c674fc2 100644 --- a/frontend/src/bundles/studio/types/types.ts +++ b/frontend/src/bundles/studio/types/types.ts @@ -13,6 +13,8 @@ export { export { type Composition, type Script as CompositionScript, + type CreateTemplateRequestDto, + type CreateTemplateResponseDto, type CreateVideoRequestDto, type GenerateSpeechRequestDto, type GenerateSpeechResponseDto, From 2f0cd8cecd2893c42200361128de0d4034ffcb40 Mon Sep 17 00:00:00 2001 From: Oleksandra Okhotnykova Date: Fri, 27 Sep 2024 09:52:26 +0200 Subject: [PATCH 10/14] OV-421: * load template on click --- .../components/template-card.tsx | 17 +++++++++++--- .../templates-content/templates-content.tsx | 4 ++-- frontend/src/bundles/studio/store/slice.ts | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx index 54450f275..a100d8fb2 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/components/template-card.tsx @@ -1,11 +1,22 @@ +import { useAppDispatch, useCallback } from '~/bundles/common/hooks/hooks.js'; import { ImageCard } from '~/bundles/studio/components/video-menu/components/menu-content/components/components.js'; +import { actions as studioActions } from '~/bundles/studio/store/studio.js'; +import { type Template } from '~/bundles/studio/types/types.js'; type Properties = { - imageSource: string; + template: Template; }; -const TemplateCard: React.FC = ({ imageSource }) => { - return ; +const TemplateCard: React.FC = ({ template }) => { + const dispatch = useAppDispatch(); + + const handleClick = useCallback((): void => { + void dispatch(studioActions.loadTemplate(template)); + }, [dispatch, template]); + + return ( + + ); }; export { TemplateCard }; diff --git a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx index bead19002..5f3879918 100644 --- a/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx +++ b/frontend/src/bundles/studio/components/video-menu/components/menu-content/templates-content/templates-content.tsx @@ -52,7 +52,7 @@ const TemplatesContent: React.FC = () => { {templates.public.map((template) => ( ))} @@ -67,7 +67,7 @@ const TemplatesContent: React.FC = () => { {templates.user.map((template) => ( ))} diff --git a/frontend/src/bundles/studio/store/slice.ts b/frontend/src/bundles/studio/store/slice.ts index 1f0633191..6f7bdac73 100644 --- a/frontend/src/bundles/studio/store/slice.ts +++ b/frontend/src/bundles/studio/store/slice.ts @@ -406,6 +406,7 @@ const { reducer, actions, name } = createSlice({ state.videoName = name; state.videoId = id; + state.videoSize = composition.videoOrientation; state.scenes = composition.scenes; state.scripts = composition.scripts.map( (script: CompositionScript) => { @@ -422,6 +423,27 @@ const { reducer, actions, name } = createSlice({ }, ); }, + loadTemplate(state, action: PayloadAction