From 65efebe1800ca17d2ee69807432fe869651c3042 Mon Sep 17 00:00:00 2001 From: arc-alex Date: Sat, 18 Nov 2023 20:58:57 +0100 Subject: [PATCH] Preview Sheet --- apps/client/src/common/api/ontimeApi.ts | 9 ++ .../modals/sheets-modal/SheetsModal.tsx | 144 +++++++++++++++--- .../src/controllers/ontimeController.ts | 13 +- .../controllers/ontimeController.validate.ts | 12 ++ apps/server/src/routes/ontimeRouter.ts | 5 +- apps/server/src/utils/sheetsAuth.ts | 4 +- 6 files changed, 150 insertions(+), 37 deletions(-) diff --git a/apps/client/src/common/api/ontimeApi.ts b/apps/client/src/common/api/ontimeApi.ts index df375519d..072e541bd 100644 --- a/apps/client/src/common/api/ontimeApi.ts +++ b/apps/client/src/common/api/ontimeApi.ts @@ -256,3 +256,12 @@ export const getSheetsAuthUrl = async () => { const res = await axios.get(`${ontimeURL}/sheet-authurl`); return res.data; }; + +export const postPreviewSheet = async (sheetId: string, worksheet: string, options?: ExcelImportMap) => { + const response = await axios.post(`${ontimeURL}/sheet-preview`, { + sheetid: sheetId, + worksheet: worksheet, + options: options, + }); + return response.data.data; +}; diff --git a/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx b/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx index 8ae65f121..84a646c34 100644 --- a/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx +++ b/apps/client/src/features/modals/sheets-modal/SheetsModal.tsx @@ -10,7 +10,21 @@ import { ModalHeader, ModalOverlay, } from '@chakra-ui/react'; -import { getSheetsAuthStatus, getSheetsAuthUrl, uploadSheetClientFile } from '../../../common/api/ontimeApi'; +import { useQueryClient } from '@tanstack/react-query'; +import { + getSheetsAuthStatus, + getSheetsAuthUrl, + postPreviewSheet, + uploadSheetClientFile, + patchData, +} from '../../../common/api/ontimeApi'; +import { OntimeRundown, ProjectData, UserFields } from 'ontime-types'; + +import PreviewExcel from '../upload-modal/preview/PreviewExcel'; +import { projectDataPlaceholder } from '../../../common/models/ProjectData'; +import { userFieldsPlaceholder } from '../../../common/models/UserFields'; +import { PROJECT_DATA, RUNDOWN_TABLE, USERFIELDS } from '../../../common/api/apiConstants'; +import { maybeAxiosError } from '../../../common/api/apiUtils'; interface SheetsModalProps { onClose: () => void; @@ -23,6 +37,14 @@ export default function SheetsModal(props: SheetsModalProps) { const [file, setFile] = useState(null); const [authState, setAuthState] = useState(true); + const [rundown, setRundown] = useState(null); + const [userFields, setUserFields] = useState(null); + const [project, setProject] = useState(null); + + const queryClient = useQueryClient(); + + const sheetid = useRef(null); + const worksheet = useRef(null); const handleClose = () => onClose(); const handleClick = () => { fileInputRef.current?.click(); @@ -53,7 +75,38 @@ export default function SheetsModal(props: SheetsModalProps) { } }); }; - const handlePullData = () => {}; + const handlePullData = () => { + postPreviewSheet(sheetid.current?.value ?? '', worksheet.current?.value ?? '').then((data) => { + setProject(data.project); + setRundown(data.rundown); + setUserFields(data.userFields); + }); + }; + + const handleFinalise = async () => { + // this step is currently only used for excel files, after preview + if (rundown && userFields && project) { + let doClose = false; + try { + await patchData({ rundown, userFields, project }); + queryClient.setQueryData(RUNDOWN_TABLE, rundown); + queryClient.setQueryData(USERFIELDS, userFields); + queryClient.setQueryData(PROJECT_DATA, project); + await queryClient.invalidateQueries({ + queryKey: [...RUNDOWN_TABLE, ...USERFIELDS, ...PROJECT_DATA], + }); + doClose = true; + } catch (error) { + const message = maybeAxiosError(error); + console.log(message); + // setErrors(`Failed applying changes ${message}`); + } finally { + if (doClose) { + handleClose(); + } + } + } + }; return ( Sheets! - -
Need to add some help here
-
- -
- {authState &&
You are authenticated
} - {!authState &&
You are not authenticated
} -
- -
+ {rundown && ( + <> + + + )} + {!rundown && ( + <> + +
Need to add some help here
+
+ +
+ {authState &&
You are authenticated
} + {!authState &&
You are not authenticated
} +
+ + +
+ + +
+
+ +
+ + )}
- + {!rundown && ( + + )} + {rundown && ( + + )}
diff --git a/apps/server/src/controllers/ontimeController.ts b/apps/server/src/controllers/ontimeController.ts index 7bb125647..d134bf917 100644 --- a/apps/server/src/controllers/ontimeController.ts +++ b/apps/server/src/controllers/ontimeController.ts @@ -397,13 +397,8 @@ export async function previewExcel(req, res) { * @returns parsed result */ export async function previewSheet(req, res) { - if (!req.body.sheetid) { - res.status(400).send({ message: 'missing sheet id' }); - return; - } try { - const options = JSON.parse(req.body.options); - const data = await Sheet.parse(req.body.sheetid, req.body.worksheet, options); + const data = await Sheet.parse(req.body.sheetid, req.body.worksheet); res.status(200).send(data); } catch (error) { res.status(500).send({ message: error.toString() }); @@ -421,13 +416,15 @@ export async function sheetClientFile(req, res) { } try { - const client = JSON.parse( fs.readFileSync(req.file.path as string, 'utf-8')); + const client = JSON.parse(fs.readFileSync(req.file.path as string, 'utf-8')); await Sheet.saveClientSecrets(client); res.status(200).send('OK'); } catch (error) { res.status(500).send({ message: error.toString() }); } - fs.unlink(req.file.path, (err) => {if(err) (logger.error(LogOrigin.Server, err.message))}); + fs.unlink(req.file.path, (err) => { + if (err) logger.error(LogOrigin.Server, err.message); + }); } /** diff --git a/apps/server/src/controllers/ontimeController.validate.ts b/apps/server/src/controllers/ontimeController.validate.ts index 8be1c8bf5..48047c57b 100644 --- a/apps/server/src/controllers/ontimeController.validate.ts +++ b/apps/server/src/controllers/ontimeController.validate.ts @@ -133,3 +133,15 @@ export const validatePatchProjectFile = [ next(); }, ]; + +//TODO: is thise correct +export const validateSheetPreview = [ + body('sheetid').isString().optional({ nullable: false }), + body('worksheet').isString().optional({ nullable: false }), + body('options').isObject().optional({ nullable: true }), + (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); + next(); + }, +]; diff --git a/apps/server/src/routes/ontimeRouter.ts b/apps/server/src/routes/ontimeRouter.ts index b738b8c22..a16a11f9d 100644 --- a/apps/server/src/routes/ontimeRouter.ts +++ b/apps/server/src/routes/ontimeRouter.ts @@ -31,6 +31,7 @@ import { validateOscSubscription, validatePatchProjectFile, validateSettings, + validateSheetPreview, validateUserFields, viewValidator, } from '../controllers/ontimeController.validate.js'; @@ -101,7 +102,5 @@ router.get('/sheet-authstatus', sheetAuthState); // create route between controller and '/ontime/sheet-authstatus' endpoint router.get('/sheet-authurl', sheetAuthUrl); -router.get('/sheet-authurl', previewSheet); - // create route between controller and '/ontime/preview-sheet' endpoint -router.get('/sheet-preview', previewSheet); \ No newline at end of file +router.post('/sheet-preview', validateSheetPreview, previewSheet); \ No newline at end of file diff --git a/apps/server/src/utils/sheetsAuth.ts b/apps/server/src/utils/sheetsAuth.ts index 3ea87ae3f..22f9d27db 100644 --- a/apps/server/src/utils/sheetsAuth.ts +++ b/apps/server/src/utils/sheetsAuth.ts @@ -6,7 +6,7 @@ import { URL } from 'url'; import { logger } from '../classes/Logger.js'; import { getAppDataPath } from '../setup.js'; import { DatabaseModel, LogOrigin } from 'ontime-types'; -import { ExcelImportOptions, isExcelImportMap } from 'ontime-utils'; +import { ExcelImportOptions, isExcelImportMap, defaultExcelImportMap } from 'ontime-utils'; import { parseExcel } from './parser.js'; import { parseProject, parseRundown, parseUserFields } from './parserFunctions.js'; import { ensureDirectory } from './fileManagement.js'; @@ -45,7 +45,7 @@ class sheet { * @returns {Promise>} * @throws */ - public async parse(sheetId: string, worksheet: string, options: ExcelImportOptions) { + public async parse(sheetId: string, worksheet: string, options = defaultExcelImportMap) { if (!sheet.client) { if (!(await this.authorized())) { throw new Error(`Sheet not authorized`);