From fa466b993391a5e9c86d81b370854004f7d2127b Mon Sep 17 00:00:00 2001 From: Jad Date: Tue, 11 Jul 2023 13:50:26 -0700 Subject: [PATCH] Add video widget crud and UI component --- .../versions/47fc88fe0477_video_widget.py | 52 +++++ met-api/src/met_api/models/__init__.py | 1 + met-api/src/met_api/models/widget_video.py | 17 +- met-api/src/met_api/resources/__init__.py | 2 + met-api/src/met_api/resources/widget_video.py | 26 ++- .../schemas/schemas/video_widget_update.json | 31 +++ .../met_api/services/widget_video_service.py | 17 +- met-web/src/apiManager/endpoints/index.ts | 5 + .../form/EngagementWidgets/Video/Form.tsx | 208 +++++++++++++++++- .../EngagementWidgets/Video/VideoContext.tsx | 45 +++- .../Video/VideoOptionCard.tsx | 8 +- .../form/EngagementWidgets/Video/index.tsx | 2 +- .../EngagementWidgets/WidgetCardSwitch.tsx | 13 ++ .../EngagementWidgets/WidgetDrawerTabs.tsx | 4 + .../EngagementWidgets/WidgetOptionCards.tsx | 4 + met-web/src/models/videoWidget.ts | 7 + .../widgetService/VideoService/index.tsx | 62 ++++++ 17 files changed, 474 insertions(+), 30 deletions(-) create mode 100644 met-api/migrations/versions/47fc88fe0477_video_widget.py create mode 100644 met-api/src/met_api/schemas/schemas/video_widget_update.json create mode 100644 met-web/src/models/videoWidget.ts create mode 100644 met-web/src/services/widgetService/VideoService/index.tsx diff --git a/met-api/migrations/versions/47fc88fe0477_video_widget.py b/met-api/migrations/versions/47fc88fe0477_video_widget.py new file mode 100644 index 000000000..d31bb95f5 --- /dev/null +++ b/met-api/migrations/versions/47fc88fe0477_video_widget.py @@ -0,0 +1,52 @@ +"""Add video widget + +Revision ID: 47fc88fe0477 +Revises: b3b5c66cea4b +Create Date: 2023-07-11 10:44:35.980432 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '47fc88fe0477' +down_revision = 'b3b5c66cea4b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('widget_video', + sa.Column('created_date', sa.DateTime(), nullable=False), + sa.Column('updated_date', sa.DateTime(), nullable=True), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('widget_id', sa.Integer(), nullable=True), + sa.Column('engagement_id', sa.Integer(), nullable=True), + sa.Column('video_url', sa.String(length=255), nullable=False), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('created_by', sa.String(length=50), nullable=True), + sa.Column('updated_by', sa.String(length=50), nullable=True), + sa.ForeignKeyConstraint(['engagement_id'], ['engagement.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['widget_id'], ['widget.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id') + ) + widget_type_table = sa.table('widget_type', + sa.Column('id', sa.Integer), + sa.Column('name', sa.String), + sa.Column('description', sa.String)) + + op.bulk_insert(widget_type_table, [ + {'id': 7, 'name': 'Video', 'description': 'Add a link to a hosted video and link preview'} + ]) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('widget_video') + + conn = op.get_bind() + + conn.execute('DELETE FROM widget_type WHERE id=7') + # ### end Alembic commands ### diff --git a/met-api/src/met_api/models/__init__.py b/met-api/src/met_api/models/__init__.py index 8b6580968..5fbe8d451 100644 --- a/met-api/src/met_api/models/__init__.py +++ b/met-api/src/met_api/models/__init__.py @@ -44,3 +44,4 @@ from .email_queue import EmailQueue from .engagement_slug import EngagementSlug from .report_setting import ReportSetting +from .widget_video import WidgetVideo diff --git a/met-api/src/met_api/models/widget_video.py b/met-api/src/met_api/models/widget_video.py index 6ba049c4a..82ad1d672 100644 --- a/met-api/src/met_api/models/widget_video.py +++ b/met-api/src/met_api/models/widget_video.py @@ -17,7 +17,7 @@ class WidgetVideo(BaseModel): # pylint: disable=too-few-public-methods, too-man id = db.Column(db.Integer, primary_key=True, autoincrement=True) widget_id = db.Column(db.Integer, ForeignKey('widget.id', ondelete='CASCADE'), nullable=True) engagement_id = db.Column(db.Integer, ForeignKey('engagement.id', ondelete='CASCADE'), nullable=True) - video_url = db.Column(db.String(), nullable=False) + video_url = db.Column(db.String(255), nullable=False) description = db.Column(db.Text()) @classmethod @@ -25,16 +25,15 @@ def get_video(cls, widget_id) -> list[WidgetVideo]: """Get video.""" widget_video = db.session.query(WidgetVideo) \ .filter(WidgetVideo.widget_id == widget_id) \ - .first() + .all() return widget_video @classmethod - def update_video(cls, widget_id, video_data: dict) -> WidgetVideo: + def update_video(cls, video_widget_id, video_data: dict) -> WidgetVideo: """Update video.""" - query = WidgetVideo.query.filter_by(WidgetVideo.widget_id == widget_id) - widget_video: WidgetVideo = query.first() - if not widget_video: - return video_data - query.update(video_data) - db.session.commit() + widget_video: WidgetVideo = WidgetVideo.query.get(video_widget_id) + if widget_video: + for key, value in video_data.items(): + setattr(widget_video, key, value) + widget_video.save() return widget_video diff --git a/met-api/src/met_api/resources/__init__.py b/met-api/src/met_api/resources/__init__.py index 0e8111ec0..2d6c78c13 100644 --- a/met-api/src/met_api/resources/__init__.py +++ b/met-api/src/met_api/resources/__init__.py @@ -45,6 +45,7 @@ from .tenant import API as TENANT_API from .engagement_slug import API as ENGAGEMENT_SLUG_API from .report_setting import API as REPORT_SETTING_API +from .widget_video import API as WIDGET_VIDEO_API __all__ = ('API_BLUEPRINT',) @@ -81,3 +82,4 @@ API.add_namespace(WIDGET_MAPS_API, path='/widgets//maps') API.add_namespace(ENGAGEMENT_SLUG_API, path='/slugs') API.add_namespace(REPORT_SETTING_API) +API.add_namespace(WIDGET_VIDEO_API, path='/widgets//videos') diff --git a/met-api/src/met_api/resources/widget_video.py b/met-api/src/met_api/resources/widget_video.py index 7d3591ecd..859ad53ff 100644 --- a/met-api/src/met_api/resources/widget_video.py +++ b/met-api/src/met_api/resources/widget_video.py @@ -20,6 +20,7 @@ from met_api.auth import jwt as _jwt from met_api.exceptions.business_exception import BusinessException +from met_api.schemas import utils as schema_utils from met_api.schemas.widget_video import WidgetVideoSchema from met_api.services.widget_video_service import WidgetVideoService from met_api.utils.roles import Role @@ -41,7 +42,7 @@ def get(widget_id): """Get video widget.""" try: widget_video = WidgetVideoService().get_video(widget_id) - return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK + return WidgetVideoSchema().dump(widget_video, many=True), HTTPStatus.OK except BusinessException as err: return str(err), err.status_code @@ -63,8 +64,31 @@ def post(widget_id): def patch(widget_id): """Update video widget.""" request_json = request.get_json() + valid_format, errors = schema_utils.validate(request_json, 'video_widget_update') + if not valid_format: + return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST try: widget_video = WidgetVideoService().update_video(widget_id, request_json) return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK except BusinessException as err: return str(err), err.status_code + +@cors_preflight('PATCH') +@API.route('/') +class Video(Resource): + """Resource for managing video widgets.""" + + @staticmethod + @cross_origin(origins=allowedorigins()) + @_jwt.has_one_of_roles([Role.EDIT_ENGAGEMENT.value]) + def patch(widget_id, video_widget_id): + """Update video widget.""" + request_json = request.get_json() + valid_format, errors = schema_utils.validate(request_json, 'video_widget_update') + if not valid_format: + return {'message': schema_utils.serialize(errors)}, HTTPStatus.BAD_REQUEST + try: + widget_video = WidgetVideoService().update_video(widget_id, video_widget_id, request_json) + return WidgetVideoSchema().dump(widget_video), HTTPStatus.OK + except BusinessException as err: + return str(err), err.status_code diff --git a/met-api/src/met_api/schemas/schemas/video_widget_update.json b/met-api/src/met_api/schemas/schemas/video_widget_update.json new file mode 100644 index 000000000..76c152338 --- /dev/null +++ b/met-api/src/met_api/schemas/schemas/video_widget_update.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://met.gov.bc.ca/.well_known/schemas/video_widget_update", + "type": "object", + "title": "The root schema", + "description": "The root schema comprises the entire JSON document.", + "default": {}, + "examples": [ + { + "description": "A video widget description", + "video_url": "https://www.youtube.com" + } + ], + "required": [], + "properties": { + "description": { + "$id": "#/properties/description", + "type": "string", + "title": "Video description", + "description": "The description of this video.", + "examples": ["A video widget description"] + }, + "video_url": { + "$id": "#/properties/video_url", + "type": "string", + "title": "Video url", + "description": "The url link to this video.", + "examples": ["https://www.youtube.com"] + } + } +} diff --git a/met-api/src/met_api/services/widget_video_service.py b/met-api/src/met_api/services/widget_video_service.py index abb2404f8..03863b9cf 100644 --- a/met-api/src/met_api/services/widget_video_service.py +++ b/met-api/src/met_api/services/widget_video_service.py @@ -1,7 +1,4 @@ """Service for Widget Video management.""" -from http import HTTPStatus - -from met_api.exceptions.business_exception import BusinessException from met_api.models.widget_video import WidgetVideo as WidgetVideoModel @@ -23,14 +20,16 @@ def create_video(widget_id, video_details: dict): return widget_video @staticmethod - def update_video(widget_id, request_json): + def update_video(widget_id, video_widget_id, video_data): """Update video widget.""" - widget_video: WidgetVideoModel = WidgetVideoModel.get_video(widget_id) + widget_video: WidgetVideoModel = WidgetVideoModel.find_by_id(video_widget_id) + if not widget_video: + raise KeyError('Video widget not found') + if widget_video.widget_id != widget_id: - raise BusinessException( - error='Invalid widgets and video', - status_code=HTTPStatus.BAD_REQUEST) - return WidgetVideoModel.update_video(widget_id, request_json) + raise ValueError('Invalid widgets and video') + + return WidgetVideoModel.update_video(widget_video.id, video_data) @staticmethod def _create_video_model(widget_id, video_data: dict): diff --git a/met-web/src/apiManager/endpoints/index.ts b/met-web/src/apiManager/endpoints/index.ts index f06f78916..8a5131685 100644 --- a/met-web/src/apiManager/endpoints/index.ts +++ b/met-web/src/apiManager/endpoints/index.ts @@ -105,6 +105,11 @@ const Endpoints = { CREATE: `${AppConfig.apiUrl}/widgets/widget_id/maps`, SHAPEFILE_PREVIEW: `${AppConfig.apiUrl}/shapefile`, }, + VideoWidgets: { + GET: `${AppConfig.apiUrl}/widgets/widget_id/videos`, + CREATE: `${AppConfig.apiUrl}/widgets/widget_id/videos`, + UPDATE: `${AppConfig.apiUrl}/widgets/widget_id/videos/video_widget_id`, + }, Tenants: { GET: `${AppConfig.apiUrl}/tenants/tenant_id`, }, diff --git a/met-web/src/components/engagement/form/EngagementWidgets/Video/Form.tsx b/met-web/src/components/engagement/form/EngagementWidgets/Video/Form.tsx index 04c8603b9..ebc9b8bef 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/Video/Form.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/Video/Form.tsx @@ -1,5 +1,207 @@ -import React from 'react'; +import React, { useContext, useEffect } from 'react'; +import Divider from '@mui/material/Divider'; +import { Grid } from '@mui/material'; +import { + MetDescription, + MetHeader3, + MetLabel, + MidScreenLoader, + PrimaryButton, + SecondaryButton, +} from 'components/common'; +import { useForm, FormProvider, SubmitHandler } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import * as yup from 'yup'; +import { useAppDispatch } from 'hooks'; +import ControlledTextField from 'components/common/ControlledInputComponents/ControlledTextField'; +import { openNotification } from 'services/notificationService/notificationSlice'; +import { WidgetDrawerContext } from '../WidgetDrawerContext'; +import { VideoContext } from './VideoContext'; +import { patchVideo, postVideo } from 'services/widgetService/VideoService'; +import { updatedDiff } from 'deep-object-diff'; -export const Form = () => { - return
Form
; +const schema = yup + .object({ + videoUrl: yup + .string() + .url('Please enter a valid Link') + .required('Please enter a valid Link') + .max(255, 'Video link cannot exceed 255 characters'), + description: yup + .string() + .required('Please enter a description') + .max(500, 'Description cannot exceed 500 characters'), + }) + .required(); + +type DetailsForm = yup.TypeOf; + +const Form = () => { + const dispatch = useAppDispatch(); + const { widget, isLoadingVideoWidget, videoWidget } = useContext(VideoContext); + const { handleWidgetDrawerOpen } = useContext(WidgetDrawerContext); + const [isCreating, setIsCreating] = React.useState(false); + + const methods = useForm({ + resolver: yupResolver(schema), + }); + + const { handleSubmit, reset } = methods; + + useEffect(() => { + if (videoWidget) { + methods.setValue('description', videoWidget.description); + methods.setValue('videoUrl', videoWidget.video_url); + } + }, [videoWidget]); + + const createVideo = async (data: DetailsForm) => { + if (!widget) { + return; + } + + const validatedData = await schema.validate(data); + const { videoUrl, description } = validatedData; + await postVideo(widget.id, { + widget_id: widget.id, + engagement_id: widget.engagement_id, + video_url: videoUrl, + description: description, + }); + dispatch(openNotification({ severity: 'success', text: 'A new video was successfully added' })); + }; + + const updateVideo = async (data: DetailsForm) => { + if (!widget || !videoWidget) { + return; + } + + const validatedData = await schema.validate(data); + const updatedDate = updatedDiff( + { + description: videoWidget.description, + video_url: videoWidget.video_url, + }, + { + description: validatedData.description, + video_url: validatedData.videoUrl, + }, + ); + + if (Object.keys(updatedDate).length === 0) { + return; + } + + await patchVideo(widget.id, videoWidget.id, { + ...updatedDate, + }); + dispatch(openNotification({ severity: 'success', text: 'The video widget was successfully updated' })); + }; + + const saveVideoWidget = (data: DetailsForm) => { + if (!videoWidget) { + return createVideo(data); + } + return updateVideo(data); + }; + const onSubmit: SubmitHandler = async (data: DetailsForm) => { + if (!widget) { + return; + } + try { + setIsCreating(true); + await saveVideoWidget(data); + setIsCreating(false); + reset({}); + handleWidgetDrawerOpen(false); + } catch (error) { + dispatch(openNotification({ severity: 'error', text: 'An error occurred while trying to add event' })); + setIsCreating(false); + } + }; + + if (isLoadingVideoWidget) { + return ( + + + + + + ); + } + + return ( + + + Video + + + + +
+ + + Description + + + + Video Link + + The video must be hosted on one of the following platforms: + + + + + + + Save & Close + + + + handleWidgetDrawerOpen(false)}> + Cancel + + + + +
+
+
+
+ ); }; + +export default Form; diff --git a/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoContext.tsx b/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoContext.tsx index cfd034583..1e402c804 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoContext.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoContext.tsx @@ -1,10 +1,15 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; import { Widget, WidgetType } from 'models/widget'; -import React, { createContext, useContext } from 'react'; import { WidgetDrawerContext } from '../WidgetDrawerContext'; import { useAppDispatch } from 'hooks'; +import { fetchVideoWidgets } from 'services/widgetService/VideoService'; +import { VideoWidget } from 'models/videoWidget'; +import { openNotification } from 'services/notificationService/notificationSlice'; export interface VideoContextProps { widget: Widget | null; + isLoadingVideoWidget: boolean; + videoWidget: VideoWidget | null; } export type EngagementParams = { @@ -13,12 +18,46 @@ export type EngagementParams = { export const VideoContext = createContext({ widget: null, + isLoadingVideoWidget: true, + videoWidget: null, }); export const VideoContextProvider = ({ children }: { children: JSX.Element | JSX.Element[] }) => { const { widgets } = useContext(WidgetDrawerContext); const dispatch = useAppDispatch(); - const widget = widgets.find((widget) => widget.widget_type_id === WidgetType.Video) || null; + const widget = widgets.find((widget) => widget.widget_type_id === WidgetType.Video) ?? null; + const [isLoadingVideoWidget, setIsLoadingVideoWidget] = useState(true); + const [videoWidget, setVideoWidget] = useState(null); - return {children}; + const loadVideoWidget = async () => { + if (!widget) { + return; + } + try { + const result = await fetchVideoWidgets(widget.id); + setVideoWidget(result[result.length - 1]); + setIsLoadingVideoWidget(false); + } catch (error) { + dispatch( + openNotification({ severity: 'error', text: 'An error occurred while trying to load video data' }), + ); + setIsLoadingVideoWidget(false); + } + }; + + useEffect(() => { + loadVideoWidget(); + }, [widget]); + + return ( + + {children} + + ); }; diff --git a/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoOptionCard.tsx b/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoOptionCard.tsx index b04038286..ee0ebb473 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoOptionCard.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/Video/VideoOptionCard.tsx @@ -12,7 +12,7 @@ import { WidgetTabValues } from '../type'; import LocationOnIcon from '@mui/icons-material/LocationOn'; import { useCreateWidgetMutation } from 'apiManager/apiSlices/widgets'; -const MapOptionCard = () => { +const VideoOptionCard = () => { const { widgets, loadWidgets, handleWidgetDrawerOpen, handleWidgetDrawerTabValueChange } = useContext(WidgetDrawerContext); const { savedEngagement } = useContext(ActionContext); @@ -32,7 +32,7 @@ const MapOptionCard = () => { await createWidget({ widget_type_id: WidgetType.Video, engagement_id: savedEngagement.id, - }); + }).unwrap(); await loadWidgets(); dispatch( openNotification({ @@ -44,7 +44,7 @@ const MapOptionCard = () => { handleWidgetDrawerTabValueChange(WidgetTabValues.VIDEO_FORM); } catch (error) { setIsCreatingWidget(false); - dispatch(openNotification({ severity: 'error', text: 'Error occurred while creating map widget' })); + dispatch(openNotification({ severity: 'error', text: 'Error occurred while creating video widget' })); handleWidgetDrawerOpen(false); } }; @@ -90,4 +90,4 @@ const MapOptionCard = () => { ); }; -export default MapOptionCard; +export default VideoOptionCard; diff --git a/met-web/src/components/engagement/form/EngagementWidgets/Video/index.tsx b/met-web/src/components/engagement/form/EngagementWidgets/Video/index.tsx index 4095e8f21..e5bbe50cf 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/Video/index.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/Video/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Form } from './Form'; import { VideoContextProvider } from './VideoContext'; +import Form from './Form'; export const VideoForm = () => { return ( diff --git a/met-web/src/components/engagement/form/EngagementWidgets/WidgetCardSwitch.tsx b/met-web/src/components/engagement/form/EngagementWidgets/WidgetCardSwitch.tsx index 881834a06..2a6e158c2 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/WidgetCardSwitch.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/WidgetCardSwitch.tsx @@ -90,6 +90,19 @@ export const WidgetCardSwitch = ({ widget, removeWidget }: WidgetCardSwitchProps }} /> + + { + removeWidget(widget.id); + }} + onEdit={() => { + handleWidgetDrawerTabValueChange(WidgetTabValues.VIDEO_FORM); + handleWidgetDrawerOpen(true); + }} + /> + ); diff --git a/met-web/src/components/engagement/form/EngagementWidgets/WidgetDrawerTabs.tsx b/met-web/src/components/engagement/form/EngagementWidgets/WidgetDrawerTabs.tsx index 7e14473f8..d33d3be47 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/WidgetDrawerTabs.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/WidgetDrawerTabs.tsx @@ -9,6 +9,7 @@ import Documents from './Documents'; import Phases from './Phases'; import EventsForm from './Events'; import MapForm from './Map'; +import VideoForm from './Video'; const WidgetDrawerTabs = () => { const { widgetDrawerTabValue } = useContext(WidgetDrawerContext); @@ -33,6 +34,9 @@ const WidgetDrawerTabs = () => { + + + ); diff --git a/met-web/src/components/engagement/form/EngagementWidgets/WidgetOptionCards.tsx b/met-web/src/components/engagement/form/EngagementWidgets/WidgetOptionCards.tsx index c3893a4e4..8a1b2efc2 100644 --- a/met-web/src/components/engagement/form/EngagementWidgets/WidgetOptionCards.tsx +++ b/met-web/src/components/engagement/form/EngagementWidgets/WidgetOptionCards.tsx @@ -7,6 +7,7 @@ import PhasesOptionCard from './Phases/PhasesOptionCard'; import SubscribeOptionCard from './Subscribe/SubscribeOptionCard'; import EventsOptionCard from './Events/EventsOptionCard'; import MapOptionCard from './Map/MapOptionCard'; +import VideoOptionCard from './Video/VideoOptionCard'; const WidgetOptionCards = () => { return ( @@ -33,6 +34,9 @@ const WidgetOptionCards = () => { + + + ); }; diff --git a/met-web/src/models/videoWidget.ts b/met-web/src/models/videoWidget.ts new file mode 100644 index 000000000..b5d681e6b --- /dev/null +++ b/met-web/src/models/videoWidget.ts @@ -0,0 +1,7 @@ +export interface VideoWidget { + id: number; + widget_id: number; + engagement_id: number; + video_url: string; + description: string; +} diff --git a/met-web/src/services/widgetService/VideoService/index.tsx b/met-web/src/services/widgetService/VideoService/index.tsx new file mode 100644 index 000000000..adc12bb01 --- /dev/null +++ b/met-web/src/services/widgetService/VideoService/index.tsx @@ -0,0 +1,62 @@ +import http from 'apiManager/httpRequestHandler'; +import Endpoints from 'apiManager/endpoints'; +import { replaceAllInURL, replaceUrl } from 'helper'; +import { VideoWidget } from 'models/videoWidget'; + +export const fetchVideoWidgets = async (widget_id: number): Promise => { + try { + const url = replaceUrl(Endpoints.VideoWidgets.GET, 'widget_id', String(widget_id)); + const responseData = await http.GetRequest(url); + return responseData.data ?? []; + } catch (err) { + return Promise.reject(err); + } +}; + +interface PostVideoRequest { + widget_id: number; + engagement_id: number; + video_url: string; + description: string; +} + +export const postVideo = async (widget_id: number, data: PostVideoRequest): Promise => { + try { + const url = replaceUrl(Endpoints.VideoWidgets.CREATE, 'widget_id', String(widget_id)); + const response = await http.PostRequest(url, data); + if (response.data) { + return response.data; + } + return Promise.reject('Failed to create video widget'); + } catch (err) { + return Promise.reject(err); + } +}; + +interface PatchVideoRequest { + video_url?: string; + description?: string; +} + +export const patchVideo = async ( + widget_id: number, + video_widget_id: number, + data: PatchVideoRequest, +): Promise => { + try { + const url = replaceAllInURL({ + URL: Endpoints.VideoWidgets.UPDATE, + params: { + widget_id: String(widget_id), + video_widget_id: String(video_widget_id), + }, + }); + const response = await http.PatchRequest(url, data); + if (response.data) { + return response.data; + } + return Promise.reject('Failed to create video widget'); + } catch (err) { + return Promise.reject(err); + } +};