mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 22:49:18 +00:00
refactor: small cleanups and type improvement
This commit is contained in:
@@ -17,18 +17,25 @@ export default function UploadFile() {
|
|||||||
|
|
||||||
const clearFile = () => {
|
const clearFile = () => {
|
||||||
setFile(null);
|
setFile(null);
|
||||||
|
setErrors('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFile = (event: ChangeEvent<HTMLInputElement>) => {
|
const handleFile = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
const fileSelected = event?.target?.files?.[0];
|
setErrors('');
|
||||||
if (!fileSelected) return;
|
|
||||||
|
|
||||||
const validate = validateFile(fileSelected);
|
const selectedFile = event?.target?.files?.[0];
|
||||||
setErrors(validate.errors?.[0]);
|
if (!selectedFile) {
|
||||||
|
setFile(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (validate.isValid) {
|
try {
|
||||||
setFile(fileSelected);
|
validateFile(selectedFile);
|
||||||
} else {
|
setFile(selectedFile);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
setErrors(error.message);
|
||||||
|
}
|
||||||
setFile(null);
|
setFile(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -47,9 +54,11 @@ export default function UploadFile() {
|
|||||||
accept='.json, .xlsx'
|
accept='.json, .xlsx'
|
||||||
data-testid='file-input'
|
data-testid='file-input'
|
||||||
/>
|
/>
|
||||||
<div className={style.uploadArea} onClick={handleClick}>
|
{!file && (
|
||||||
Click to upload Ontime project or xlsx file
|
<div className={style.uploadArea} onClick={handleClick}>
|
||||||
</div>
|
Click to upload Ontime project or xlsx file
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{(file || errors) && (
|
{(file || errors) && (
|
||||||
<UploadEntry file={file} errors={errors} progress={progress} success={success} handleClear={clearFile} />
|
<UploadEntry file={file} errors={errors} progress={progress} success={success} handleClear={clearFile} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,14 +10,18 @@ import {
|
|||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { OntimeRundown } from 'ontime-types';
|
import { OntimeRundown, ProjectData, UserFields } from 'ontime-types';
|
||||||
|
import { defaultExcelImportMap, ExcelImportMap } from 'ontime-utils';
|
||||||
|
|
||||||
import { RUNDOWN_TABLE } from '../../../common/api/apiConstants';
|
import { invalidateAllCaches, maybeAxiosError } from '../../../common/api/apiUtils';
|
||||||
import { maybeAxiosError } from '../../../common/api/apiUtils';
|
import {
|
||||||
import { postPreviewExcel, uploadData } from '../../../common/api/ontimeApi';
|
patchData,
|
||||||
|
postPreviewExcel,
|
||||||
|
ProjectFileImportOptions,
|
||||||
|
uploadProjectFile,
|
||||||
|
} from '../../../common/api/ontimeApi';
|
||||||
import { projectDataPlaceholder } from '../../../common/models/ProjectData';
|
import { projectDataPlaceholder } from '../../../common/models/ProjectData';
|
||||||
import { userFieldsPlaceholder } from '../../../common/models/UserFields';
|
import { userFieldsPlaceholder } from '../../../common/models/UserFields';
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
|
||||||
|
|
||||||
import PreviewExcel from './preview/PreviewExcel';
|
import PreviewExcel from './preview/PreviewExcel';
|
||||||
import ExcelFileOptions from './upload-options/ExcelFileOptions';
|
import ExcelFileOptions from './upload-options/ExcelFileOptions';
|
||||||
@@ -25,20 +29,12 @@ import OntimeFileOptions from './upload-options/OntimeFileOptions';
|
|||||||
import UploadStepTracker from './upload-step/UploadStep';
|
import UploadStepTracker from './upload-step/UploadStep';
|
||||||
import UploadFile from './UploadFile';
|
import UploadFile from './UploadFile';
|
||||||
import { useUploadModalContextStore } from './uploadModalContext';
|
import { useUploadModalContextStore } from './uploadModalContext';
|
||||||
import { defaultExcelImportMap, ExcelImportMapKeys, isExcelFile, isOntimeFile } from './uploadUtils';
|
import { isExcelFile, isOntimeFile } from './uploadUtils';
|
||||||
|
|
||||||
import style from './UploadModal.module.scss';
|
import style from './UploadModal.module.scss';
|
||||||
|
|
||||||
export type UploadStep = 'upload' | 'review';
|
export type UploadStep = 'upload' | 'review';
|
||||||
|
|
||||||
export interface OntimeInputOptions {
|
|
||||||
onlyImportRundown?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ExcelInputOptions = {
|
|
||||||
[K in ExcelImportMapKeys]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface UploadModalProps {
|
interface UploadModalProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -51,67 +47,76 @@ export default function UploadModal({ onClose, isOpen }: UploadModalProps) {
|
|||||||
|
|
||||||
const [uploadStep, setUploadStep] = useState<UploadStep>('upload');
|
const [uploadStep, setUploadStep] = useState<UploadStep>('upload');
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [rundown, setRundown] = useState<OntimeRundown>([]);
|
const [rundown, setRundown] = useState<OntimeRundown | null>(null);
|
||||||
const [userFields, setUserFields] = useState(userFieldsPlaceholder);
|
const [userFields, setUserFields] = useState<UserFields | null>(null);
|
||||||
const [project, setProject] = useState(projectDataPlaceholder);
|
const [project, setProject] = useState<ProjectData | null>(null);
|
||||||
|
|
||||||
const [errors, setErrors] = useState('');
|
const [errors, setErrors] = useState('');
|
||||||
|
|
||||||
const ontimeFileOptions = useRef<Partial<OntimeInputOptions>>({});
|
const ontimeFileOptions = useRef<Partial<ProjectFileImportOptions>>({});
|
||||||
const excelFileOptions = useRef<Partial<ExcelInputOptions>>(defaultExcelImportMap);
|
const excelFileOptions = useRef<ExcelImportMap>(defaultExcelImportMap);
|
||||||
|
|
||||||
|
/* if the modal re-opens, we want to restart all states */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clear();
|
clear();
|
||||||
setUploadStep('upload');
|
setUploadStep('upload');
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
setRundown([]);
|
setRundown(null);
|
||||||
setUserFields(userFieldsPlaceholder);
|
setUserFields(null);
|
||||||
setProject(projectDataPlaceholder);
|
setProject(null);
|
||||||
setErrors('');
|
setErrors('');
|
||||||
}, [clear, isOpen]);
|
}, [clear, isOpen]);
|
||||||
|
|
||||||
const handleParse = async () => {
|
/* uploads file to backend
|
||||||
|
* - in the case of excel, we get the preview
|
||||||
|
* - in the case of project file, this is end of line
|
||||||
|
**/
|
||||||
|
const handleUpload = async () => {
|
||||||
|
let doClose = false;
|
||||||
if (file) {
|
if (file) {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
setErrors('');
|
||||||
try {
|
try {
|
||||||
if (isOntimeFile(file)) {
|
if (isOntimeFile(file)) {
|
||||||
await handleOntimeFile(file);
|
// TODO: we would also like to have preview for ontime project files
|
||||||
await queryClient.invalidateQueries(RUNDOWN_TABLE);
|
const options = ontimeFileOptions.current;
|
||||||
|
await handleOntimeFile(file, options);
|
||||||
|
doClose = true;
|
||||||
} else if (isExcelFile(file)) {
|
} else if (isExcelFile(file)) {
|
||||||
await handleExcelFile(file);
|
const options = excelFileOptions.current;
|
||||||
}
|
await handleExcelFile(file, options);
|
||||||
} catch (error) {
|
await invalidateAllCaches();
|
||||||
setErrors(`Failed uploading file: ${error}`);
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleExcelFile(file: File) {
|
|
||||||
const options = excelFileOptions.current;
|
|
||||||
// TODO: option type should be central, to also be used by backend
|
|
||||||
try {
|
|
||||||
const response = await postPreviewExcel(file, setProgress, options);
|
|
||||||
if (response.status === 200) {
|
|
||||||
setRundown(response.data.rundown);
|
|
||||||
setUserFields(response.data.userFields);
|
|
||||||
setProject(response.data.project);
|
|
||||||
setUploadStep('review');
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = maybeAxiosError(error);
|
const message = maybeAxiosError(error);
|
||||||
setErrors(`Error importing excel ${message}`);
|
setErrors(`Failed uploading file ${message}`);
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
if (doClose) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleOntimeFile(file: File) {
|
// when we upload excel, we populate state with preview data
|
||||||
const options = {
|
async function handleExcelFile(file: File, options: ExcelImportMap) {
|
||||||
onlyRundown: Boolean(ontimeFileOptions.current.onlyImportRundown),
|
const response = await postPreviewExcel(file, setProgress, options);
|
||||||
};
|
if (response.status === 200) {
|
||||||
await uploadData(file, setProgress, options);
|
setRundown(response.data.rundown);
|
||||||
|
setUserFields(response.data.userFields);
|
||||||
|
setProject(response.data.project);
|
||||||
|
// in excel imports we have an extra review step
|
||||||
|
setUploadStep('review');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when we upload project files, no extra operations are done
|
||||||
|
async function handleOntimeFile(file: File, options: Partial<ProjectFileImportOptions>) {
|
||||||
|
await uploadProjectFile(file, setProgress, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// before closing the modal, we clear data from mutations
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
clear();
|
clear();
|
||||||
setRundown([]);
|
setRundown([]);
|
||||||
@@ -121,36 +126,42 @@ export default function UploadModal({ onClose, isOpen }: UploadModalProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleFinalise = async () => {
|
const handleFinalise = async () => {
|
||||||
if (file) {
|
// this step is currently only used for excel files, after preview
|
||||||
|
if (isExcel && rundown && userFields && project) {
|
||||||
|
let doClose = false;
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const options = {
|
await patchData({ rundown, userFields, project });
|
||||||
//onlyRundown: overrideOptionRef.current?.checked || false,
|
await queryClient.invalidateQueries(['rundown', 'userFields', 'project']);
|
||||||
};
|
doClose = true;
|
||||||
await uploadData(file, setProgress, options);
|
|
||||||
handleClose();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
const message = maybeAxiosError(error);
|
||||||
|
setErrors(`Failed applying changes ${message}`);
|
||||||
} finally {
|
} finally {
|
||||||
await queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
if (doClose) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const undoReview = () => {
|
||||||
|
setUploadStep('upload');
|
||||||
|
setErrors('');
|
||||||
|
};
|
||||||
|
|
||||||
const isUpload = uploadStep === 'upload';
|
const isUpload = uploadStep === 'upload';
|
||||||
|
const isReview = uploadStep === 'review';
|
||||||
const isExcel = isExcelFile(file);
|
const isExcel = isExcelFile(file);
|
||||||
const isOntime = isOntimeFile(file);
|
const isOntime = isOntimeFile(file);
|
||||||
|
|
||||||
const handleGoBack = isUpload ? undefined : () => setUploadStep('upload');
|
const handleGoBack = isUpload ? undefined : undoReview;
|
||||||
const handleSubmit = isUpload ? handleParse : handleFinalise;
|
const handleSubmit = isUpload ? handleUpload : handleFinalise;
|
||||||
const disableSubmit = isUpload && !file;
|
const disableSubmit = (isUpload && !file) || (isReview && rundown === null);
|
||||||
const disableGoBack = isUpload;
|
const disableGoBack = isUpload;
|
||||||
const submitText = isUpload ? 'Upload' : 'Finish';
|
const submitText = isUpload ? 'Upload' : 'Finish';
|
||||||
|
|
||||||
const modalClasses = cx([style.modalWidthOverride, isExcel ? style.doExtend : null]);
|
|
||||||
|
|
||||||
console.log('debug', isExcel, modalClasses);
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
@@ -171,33 +182,40 @@ export default function UploadModal({ onClose, isOpen }: UploadModalProps) {
|
|||||||
{uploadStep === 'upload' ? (
|
{uploadStep === 'upload' ? (
|
||||||
<>
|
<>
|
||||||
<UploadFile />
|
<UploadFile />
|
||||||
{errors && <div className={style.error}>{errors}</div>}
|
|
||||||
{isOntime && <OntimeFileOptions optionsRef={ontimeFileOptions} />}
|
{isOntime && <OntimeFileOptions optionsRef={ontimeFileOptions} />}
|
||||||
{isExcel && <ExcelFileOptions optionsRef={excelFileOptions} />}
|
{isExcel && <ExcelFileOptions optionsRef={excelFileOptions} />}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<PreviewExcel rundown={rundown} project={project} userFields={userFields} />
|
<PreviewExcel
|
||||||
|
rundown={rundown ?? []}
|
||||||
|
project={project ?? projectDataPlaceholder}
|
||||||
|
userFields={userFields ?? userFieldsPlaceholder}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter className={`${style.buttonSection} ${style.pad}`}>
|
<ModalFooter>
|
||||||
<Button
|
<div className={style.feedbackSection}>{errors && <div className={style.error}>{errors}</div>}</div>
|
||||||
onClick={handleGoBack}
|
|
||||||
isDisabled={disableGoBack || submitting}
|
<div className={`${style.buttonSection} ${style.pad}`}>
|
||||||
variant='ontime-ghost-on-light'
|
<Button
|
||||||
size='sm'
|
onClick={handleGoBack}
|
||||||
>
|
isDisabled={disableGoBack || submitting}
|
||||||
Go Back
|
variant='ontime-ghost-on-light'
|
||||||
</Button>
|
size='sm'
|
||||||
<Button
|
>
|
||||||
onClick={handleSubmit}
|
Go Back
|
||||||
isLoading={submitting}
|
</Button>
|
||||||
isDisabled={disableSubmit}
|
<Button
|
||||||
variant='ontime-filled'
|
onClick={handleSubmit}
|
||||||
padding='0 2em'
|
isLoading={submitting}
|
||||||
size='sm'
|
isDisabled={disableSubmit}
|
||||||
>
|
variant='ontime-filled'
|
||||||
{submitText}
|
padding='0 2em'
|
||||||
</Button>
|
size='sm'
|
||||||
|
>
|
||||||
|
{submitText}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -1,35 +1,32 @@
|
|||||||
import { MutableRefObject } from 'react';
|
import { MutableRefObject } from 'react';
|
||||||
import { Switch } from '@chakra-ui/react';
|
import { Switch } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import { ProjectFileImportOptions } from '../../../../common/api/ontimeApi';
|
||||||
import ModalSplitInput from '../../ModalSplitInput';
|
import ModalSplitInput from '../../ModalSplitInput';
|
||||||
import { OntimeInputOptions } from '../UploadModal';
|
|
||||||
|
|
||||||
import style from '../UploadModal.module.scss';
|
import style from '../UploadModal.module.scss';
|
||||||
|
|
||||||
interface OntimeFileOptionsProps {
|
interface OntimeFileOptionsProps {
|
||||||
optionsRef: MutableRefObject<OntimeInputOptions>;
|
optionsRef: MutableRefObject<Partial<ProjectFileImportOptions>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function OntimeFileOptions(props: OntimeFileOptionsProps) {
|
export default function OntimeFileOptions(props: OntimeFileOptionsProps) {
|
||||||
const { optionsRef } = props;
|
const { optionsRef } = props;
|
||||||
|
|
||||||
const updateRef = <T extends keyof OntimeInputOptions>(field: T, value: OntimeInputOptions[T]) => {
|
const updateRef = <T extends keyof ProjectFileImportOptions>(field: T, value: ProjectFileImportOptions[T]) => {
|
||||||
optionsRef.current = { ...optionsRef.current, [field]: value };
|
optionsRef.current = { ...optionsRef.current, [field]: value };
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.uploadOptions}>
|
<div className={style.uploadOptions}>
|
||||||
<span className={style.title}>Import options</span>
|
<span className={style.title}>Import options</span>
|
||||||
<ModalSplitInput
|
<ModalSplitInput field='' title='Only import rundown' description='All other project options will be kept'>
|
||||||
field=''
|
|
||||||
title='Only import rundown'
|
|
||||||
description='All other options, including application settings will be discarded'
|
|
||||||
>
|
|
||||||
<Switch
|
<Switch
|
||||||
variant='ontime-on-light'
|
variant='ontime-on-light'
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
updateRef('onlyImportRundown', e.target.checked);
|
updateRef('onlyRundown', e.target.checked);
|
||||||
}}
|
}}
|
||||||
|
defaultChecked={Boolean(optionsRef.current.onlyRundown)}
|
||||||
/>
|
/>
|
||||||
</ModalSplitInput>
|
</ModalSplitInput>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,33 +1,22 @@
|
|||||||
type ValidationStatus = {
|
export function validateFile(file: File) {
|
||||||
errors: string[];
|
|
||||||
isValid: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function validateFile(file: File): ValidationStatus {
|
|
||||||
const status: ValidationStatus = { errors: [], isValid: true };
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
status.errors.push('No file to upload');
|
throw new Error('No file to upload');
|
||||||
status.isValid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit file size of a project file to around 1MB
|
// Limit file size of a project file to around 1MB
|
||||||
if (file.name.endsWith('.json') && file.size > 1_000_000) {
|
if (file.name.endsWith('.json') && file.size > 1_000_000) {
|
||||||
status.errors.push('File size limit (1MB) exceeded');
|
throw new Error('File size limit (1MB) exceeded');
|
||||||
status.isValid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit file size of an excel file to around 10MB
|
// Limit file size of an excel file to around 10MB
|
||||||
if (file.name.endsWith('.xlsx') && file.size > 10_000_000) {
|
if (file.name.endsWith('.xlsx') && file.size > 10_000_000) {
|
||||||
status.errors.push('File size limit (10MB) exceeded');
|
throw new Error('File size limit (10MB) exceeded');
|
||||||
status.isValid = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check file extension
|
// Check file extension
|
||||||
if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.json')) {
|
if (!file.name.endsWith('.xlsx') && !file.name.endsWith('.json')) {
|
||||||
status.errors.push('Unhandled file type');
|
throw new Error('Unhandled file type');
|
||||||
status.isValid = false;
|
|
||||||
}
|
}
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isExcelFile(file: File | null) {
|
export function isExcelFile(file: File | null) {
|
||||||
|
|||||||
@@ -2,7 +2,16 @@
|
|||||||
* Class Event Provider is a mediator for handling the local db
|
* Class Event Provider is a mediator for handling the local db
|
||||||
* and adds logic specific to ontime data
|
* and adds logic specific to ontime data
|
||||||
*/
|
*/
|
||||||
import { ProjectData, OntimeRundown, ViewSettings } from 'ontime-types';
|
import {
|
||||||
|
ProjectData,
|
||||||
|
OntimeRundown,
|
||||||
|
ViewSettings,
|
||||||
|
DatabaseModel,
|
||||||
|
OSCSettings,
|
||||||
|
UserFields,
|
||||||
|
Alias,
|
||||||
|
Settings,
|
||||||
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { data, db } from '../../modules/loadDb.js';
|
import { data, db } from '../../modules/loadDb.js';
|
||||||
import { safeMerge } from './DataProvider.utils.js';
|
import { safeMerge } from './DataProvider.utils.js';
|
||||||
@@ -45,7 +54,7 @@ export class DataProvider {
|
|||||||
return data.settings;
|
return data.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setSettings(newData) {
|
static async setSettings(newData: Settings) {
|
||||||
data.settings = { ...newData };
|
data.settings = { ...newData };
|
||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
@@ -58,7 +67,7 @@ export class DataProvider {
|
|||||||
return data.aliases;
|
return data.aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setAliases(newData) {
|
static async setAliases(newData: Alias[]) {
|
||||||
data.aliases = newData;
|
data.aliases = newData;
|
||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
@@ -76,12 +85,12 @@ export class DataProvider {
|
|||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setUserFields(newData) {
|
static async setUserFields(newData: UserFields) {
|
||||||
data.userFields = { ...newData };
|
data.userFields = { ...newData };
|
||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async setOsc(newData) {
|
static async setOsc(newData: OSCSettings) {
|
||||||
data.osc = { ...newData };
|
data.osc = { ...newData };
|
||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
@@ -95,7 +104,7 @@ export class DataProvider {
|
|||||||
await db.write();
|
await db.write();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async mergeIntoData(newData) {
|
static async mergeIntoData(newData: Partial<DatabaseModel>) {
|
||||||
const mergedData = safeMerge(data, newData);
|
const mergedData = safeMerge(data, newData);
|
||||||
data.project = mergedData.project;
|
data.project = mergedData.project;
|
||||||
data.settings = mergedData.settings;
|
data.settings = mergedData.settings;
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import { DatabaseModel } from 'ontime-types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges two data objects
|
* Merges two data objects
|
||||||
* @param {object} existing
|
* @param {object} existing
|
||||||
* @param {object} newData
|
* @param {object} newData
|
||||||
*/
|
*/
|
||||||
export function safeMerge(existing, newData) {
|
export function safeMerge(existing: DatabaseModel, newData: Partial<DatabaseModel>) {
|
||||||
const { rundown, project, settings, viewSettings, osc, http, aliases, userFields } = newData || {};
|
const { rundown, project, settings, viewSettings, osc, aliases, userFields } = newData || {};
|
||||||
return {
|
return {
|
||||||
...existing,
|
...existing,
|
||||||
rundown: rundown ?? existing.rundown,
|
rundown: rundown ?? existing.rundown,
|
||||||
@@ -32,6 +34,5 @@ export function safeMerge(existing, newData) {
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
http: { ...existing.http, ...http },
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,6 @@ describe('safeMerge', () => {
|
|||||||
onFinish: [],
|
onFinish: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
http: {
|
|
||||||
enabled: true,
|
|
||||||
user: null,
|
|
||||||
pwd: null,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it('returns existing data if new data is not provided', () => {
|
it('returns existing data if new data is not provided', () => {
|
||||||
@@ -188,19 +183,6 @@ describe('safeMerge', () => {
|
|||||||
onFinish: [],
|
onFinish: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
http: {
|
|
||||||
user: null,
|
|
||||||
pwd: null,
|
|
||||||
messages: {
|
|
||||||
onLoad: [],
|
|
||||||
onStart: [],
|
|
||||||
onUpdate: [],
|
|
||||||
onPause: [],
|
|
||||||
onStop: [],
|
|
||||||
onFinish: [],
|
|
||||||
},
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const newData = {
|
const newData = {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const parseDb = async (fileToRead, adapterToUse) => {
|
|||||||
adapterToUse.data = dbModel;
|
adapterToUse.data = dbModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseJson(adapterToUse.data, true);
|
return parseJson(adapterToUse.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -287,13 +287,13 @@ export function notifyChanges(options: { timer?: boolean | string[]; external?:
|
|||||||
updateTimer();
|
updateTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.external) {
|
|
||||||
// advice socket subscribers of change
|
|
||||||
sendRefetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.reset) {
|
if (options.reset) {
|
||||||
// force rundown to be recalculated
|
// force rundown to be recalculated
|
||||||
forceReset();
|
forceReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.external) {
|
||||||
|
// advice socket subscribers of change
|
||||||
|
sendRefetch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user