diff --git a/src/api/manager/multiviews.ts b/src/api/manager/multiviews.ts index d7d0713b..d4121e2a 100644 --- a/src/api/manager/multiviews.ts +++ b/src/api/manager/multiviews.ts @@ -1,6 +1,7 @@ import { ObjectId, WithId } from 'mongodb'; import { TMultiviewLayout } from '../../interfaces/preset'; import { getDatabase } from '../mongoClient/dbClient'; +import { Log } from '../logger'; export async function getMultiviewLayouts(): Promise { const db = await getDatabase(); @@ -38,3 +39,12 @@ export async function putMultiviewLayout( await collection.insertOne({ ...newMultiviewLayout, _id: new ObjectId() }); } } + +export async function deleteLayout(id: string): Promise { + const db = await getDatabase(); + + await db.collection('multiviews').deleteOne({ + _id: { $eq: new ObjectId(id) } + }); + Log().info('Deleted multiview layout', id); +} diff --git a/src/app/api/manager/multiviews/[id]/route.ts b/src/app/api/manager/multiviews/[id]/route.ts index 139ff567..56d8af54 100644 --- a/src/app/api/manager/multiviews/[id]/route.ts +++ b/src/app/api/manager/multiviews/[id]/route.ts @@ -3,7 +3,10 @@ import { isAuthenticated } from '../../../../../api/manager/auth'; import { Params } from 'next/dist/shared/lib/router/utils/route-matcher'; import { updateMultiviewForPipeline } from '../../../../../api/ateliereLive/pipelines/multiviews/multiviews'; import { MultiviewViews } from '../../../../../interfaces/multiview'; -import { getMultiviewLayout } from '../../../../../api/manager/multiviews'; +import { + deleteLayout, + getMultiviewLayout +} from '../../../../../api/manager/multiviews'; type PutMultiviewRequest = { pipelineId: string; @@ -52,3 +55,28 @@ export async function PUT( }); } } + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + try { + await deleteLayout(params.id); + return new NextResponse(null, { + status: 200 + }); + } catch (error) { + console.log(error); + return new NextResponse( + `Error occurred while deleting from DB! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx index 9d345135..008f0024 100644 --- a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx @@ -45,7 +45,7 @@ export default function MultiviewLayout({ id: singleSource.id, label: singleSource.label }))} - value={label || ''} + value="" update={(value) => handleChange(id || '', value)} columnStyle /> diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx index 5e370148..51006cce 100644 --- a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx @@ -3,7 +3,10 @@ import { useMultiviewPresets } from '../../../../hooks/multiviewPreset'; import { MultiviewPreset } from '../../../../interfaces/preset'; import { useTranslate } from '../../../../i18n/useTranslate'; import { useSetupMultiviewLayout } from '../../../../hooks/useSetupMultiviewLayout'; -import { useMultiviewLayouts } from '../../../../hooks/multiviewLayout'; +import { + useDeleteMultiviewLayout, + useMultiviewLayouts +} from '../../../../hooks/multiviewLayout'; import { Production } from '../../../../interfaces/production'; import { useConfigureMultiviewLayout } from '../../../../hooks/useConfigureMultiviewLayout'; import { TMultiviewLayout } from '../../../../interfaces/preset'; @@ -12,6 +15,9 @@ import { TListSource } from '../../../../interfaces/multiview'; import Options from '../../configureOutputModal/Options'; import Input from '../../configureOutputModal/Input'; import MultiviewLayout from './MultiviewLayout'; +import { IconTrash } from '@tabler/icons-react'; +import toast from 'react-hot-toast'; +import { set } from 'lodash'; type ChangeLayout = { defaultLabel?: string; @@ -28,10 +34,11 @@ export default function MultiviewLayoutSettings({ }) { const [selectedMultiviewPreset, setSelectedMultiviewPreset] = useState(null); + const [refresh, setRefresh] = useState(true); const [changedLayout, setChangedLayout] = useState(null); const [newPresetName, setNewPresetName] = useState(null); const [layoutToChange, setLayoutToChange] = useState(''); - const [multiviewLayouts] = useMultiviewLayouts(); + const [multiviewLayouts] = useMultiviewLayouts(refresh); const [multiviewPresets] = useMultiviewPresets(); const { multiviewPresetLayout } = useSetupMultiviewLayout( selectedMultiviewPreset @@ -45,6 +52,7 @@ export default function MultiviewLayoutSettings({ newPresetName ); const { inputList } = useCreateInputArray(production); + const deleteLayout = useDeleteMultiviewLayout(); const t = useTranslate(); const multiviewPresetNames = multiviewPresets?.map((preset) => preset.name) @@ -57,6 +65,15 @@ export default function MultiviewLayoutSettings({ const multiviewLayoutNames = availableMultiviewLayouts?.map((layout) => layout.name) || []; + const productionLayouts = + multiviewLayouts?.filter( + (layout) => layout.productionId === production?._id + ) || []; + const globalMultiviewLayouts = multiviewLayouts?.filter( + (layout) => !layout.productionId + ); + const deleteDisabled = productionLayouts.length < 1; + // This useEffect is used to set the drawn layout of the multiviewer on start, // if this fails then the modal will be empty useEffect(() => { @@ -65,6 +82,12 @@ export default function MultiviewLayoutSettings({ } }, [multiviewPresets]); + useEffect(() => { + if (multiviewLayouts) { + setRefresh(false); + } + }, [multiviewLayouts]); + const layoutNameAlreadyExist = availableMultiviewLayouts?.find( (singlePreset) => singlePreset.name === multiviewLayout?.name )?.name; @@ -111,6 +134,7 @@ export default function MultiviewLayoutSettings({ const defaultLabel = availableMultiviewLayouts[0].layout.views.find( (item) => item.input_slot === idFirstInputView )?.label; + inputList.map((source) => { if (value === '') { setChangedLayout({ defaultLabel, viewId }); @@ -122,21 +146,68 @@ export default function MultiviewLayoutSettings({ } }; + const removeMultiviewLayout = () => { + const layoutToRemove = productionLayouts.find( + (layout) => layout.name === newPresetName + ); + const globalLayoutToRemove = globalMultiviewLayouts?.find( + (layout) => layout.name === newPresetName + ); + + if (!layoutToRemove && globalLayoutToRemove) { + toast.error(t('preset.not_possible_delete_global_layout')); + return; + } + if (layoutToRemove && !layoutToRemove._id) { + toast.error(t('preset.could_not_delete_layout')); + return; + } + if (layoutToRemove && layoutToRemove._id) { + deleteLayout(layoutToRemove._id.toString()).then(() => { + setRefresh(true); + if (multiviewPresets && multiviewPresets[0]) { + setSelectedMultiviewPreset(multiviewPresets[0]); + } + setNewPresetName(''); + toast.success(t('preset.layout_deleted')); + }); + } + }; + return ( <> {selectedMultiviewPreset && (
- ({ - label: singleItem - }))} - value={ - selectedMultiviewPreset ? selectedMultiviewPreset.name : '' - } - update={(value) => handleLayoutUpdate(value, 'layout')} - /> +
+ ({ + label: singleItem + }))} + value={ + selectedMultiviewPreset ? selectedMultiviewPreset.name : '' + } + update={(value) => handleLayoutUpdate(value, 'layout')} + /> + {!production?.isActive && ( + + )} +
({ diff --git a/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx index 9865bd1e..48bea1eb 100644 --- a/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx @@ -27,7 +27,7 @@ export default function MultiviewSettingsConfig({ productionId }: MultiviewSettingsProps) { const t = useTranslate(); - const [multiviewLayouts] = useMultiviewLayouts(); + const [multiviewLayouts] = useMultiviewLayouts(true); const [selectedMultiviewLayout, setSelectedMultiviewLayout] = useState< TMultiviewLayout | undefined >(); diff --git a/src/components/modal/configureOutputModal/Options.tsx b/src/components/modal/configureOutputModal/Options.tsx index bf534522..2a8a3f76 100644 --- a/src/components/modal/configureOutputModal/Options.tsx +++ b/src/components/modal/configureOutputModal/Options.tsx @@ -32,16 +32,16 @@ export default function Options({ onChange={(e) => { update(e.target.value); }} - value={value} + value={value === '' && columnStyle ? undefined : value} className="cursor-pointer px-2 border justify-center text-sm rounded-lg w-6/12 pt-1 pb-1 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:border-gray-400 focus:outline-none" > - {columnStyle && } - {options.map((value, i) => ( + {columnStyle && } + {options.map((option, i) => ( ))} diff --git a/src/components/startProduction/StartProductionButton.tsx b/src/components/startProduction/StartProductionButton.tsx index 3f7944c4..4d9f5d6f 100644 --- a/src/components/startProduction/StartProductionButton.tsx +++ b/src/components/startProduction/StartProductionButton.tsx @@ -41,7 +41,7 @@ export function StartProductionButton({ const [deleteMonitoring] = useDeleteMonitoring(); const [modalOpen, setModalOpen] = useState(false); const [stopModalOpen, setStopModalOpen] = useState(false); - const [multiviewLayouts] = useMultiviewLayouts(); + const [multiviewLayouts] = useMultiviewLayouts(true); const onClick = () => { if (!production) return; diff --git a/src/hooks/multiviewLayout.ts b/src/hooks/multiviewLayout.ts index 13525d1a..b971e3ad 100644 --- a/src/hooks/multiviewLayout.ts +++ b/src/hooks/multiviewLayout.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { TMultiviewLayout } from '../interfaces/preset'; import { DataHook } from './types'; import { WithId } from 'mongodb'; @@ -30,13 +30,21 @@ export function useGetMultiviewLayout() { }; } -export function useMultiviewLayouts(): DataHook { +export function useMultiviewLayouts( + refresh: boolean +): DataHook { const [loading, setLoading] = useState(true); const [multiviewLayouts, setmultiviewLayouts] = useState( [] ); useEffect(() => { + setmultiviewLayouts([]); + + if (!refresh) { + return; + } + setLoading(true); fetch('/api/manager/multiviews', { method: 'GET', @@ -48,7 +56,7 @@ export function useMultiviewLayouts(): DataHook { } }) .finally(() => setLoading(false)); - }, []); + }, [refresh]); return [multiviewLayouts, loading, undefined]; } @@ -66,3 +74,16 @@ export function usePutMultiviewLayout() { throw await response.text(); }; } + +export function useDeleteMultiviewLayout() { + return async (id: string): Promise => { + const response = await fetch(`/api/manager/multiviews/${id}`, { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }); + if (response.ok) { + return; + } + throw await response.text(); + }; +} diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 3d59fb00..fcf3ed18 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -682,7 +682,11 @@ export const en = { layout_already_exist: 'Layout {{layoutNameAlreadyExist}} will be replaced on save', remove_multiview: 'Remove multiview', + remove_layout: 'Remove layout', add_another_multiview: 'Add another multiview', + not_possible_delete_global_layout: 'Global layout can not be deleted', + could_not_delete_layout: 'Could not delete layout', + layout_deleted: 'Layout deleted', confirm_update_multiviewers: 'Are you sure you want to update multiviewers for the running production?', confirm_update: 'Update multiviewers' diff --git a/src/i18n/locales/sv.ts b/src/i18n/locales/sv.ts index ae598f3e..990efe7c 100644 --- a/src/i18n/locales/sv.ts +++ b/src/i18n/locales/sv.ts @@ -686,7 +686,12 @@ export const sv = { layout_already_exist: 'Konfigurationen {{layoutNameAlreadyExist}} skrivs över om du sparar', remove_multiview: 'Ta bort multiview', + remove_layout: 'Ta bort komposition', add_another_multiview: 'Lägg till ny multiview', + layout_deleted: 'Kompositionen har tagits bort', + not_possible_delete_global_layout: + 'Det går inte att ta bort globala kompositioner', + could_not_delete_layout: 'Kunde inte ta bort kompositionen', confirm_update_multiviewers: 'Är du säker på att du vill uppdatera multiview för pågående produktion?', confirm_update: 'Uppdatera multiviewers'