Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

1015 wiersze
30 KiB

  1. import { toast } from 'react-toastify';
  2. import DialogTitle from '@mui/material/DialogTitle';
  3. import DialogContent from '@mui/material/DialogContent';
  4. import DialogContentText from '@mui/material/DialogContentText';
  5. import DialogActions from '@mui/material/DialogActions';
  6. import { Button, Grid, TextField, Typography, InputLabel, FormControl } from '@mui/material';
  7. import Dialog from '@mui/material/Dialog';
  8. import * as React from 'react';
  9. import Autocomplete from '@mui/material/Autocomplete';
  10. import {useEffect, useRef, useState} from 'react';
  11. import { format } from 'date-fns';
  12. import { GridOverlay } from '@mui/x-data-grid';
  13. import axios from 'axios';
  14. import {apiPath, getUserData} from '../auth/utils';
  15. import {GENERAL_RED_COLOR, GENERAL_TEXT_COLOR} from '../themes/colorConst';
  16. // import { Route } from '@mui/icons-material';
  17. import { MuiFileInput } from 'mui-file-input';
  18. import { POST_THUMBNAIL_PATH } from './ApiPathConst';
  19. import { useForm } from 'react-hook-form';
  20. import {isObjEmpty} from "./Utils";
  21. import AttachFileIcon from '@mui/icons-material/AttachFile'
  22. import CloudUploadIcon from '@mui/icons-material/CloudUpload';
  23. //====================COMMON FUNCTION====================//
  24. export function getUserAuthList() {
  25. const abilities = localStorage.getItem('abilities');
  26. if (abilities !== null) {
  27. const abilitiesList = abilities.split(',');
  28. return abilitiesList;
  29. }
  30. return [];
  31. }
  32. export function handleRouteAbility(canAccess, targetPage, redirectPage) {
  33. return canAccess ? targetPage : redirectPage;
  34. }
  35. export function readFile(input, curBlob, setCurBlob) {
  36. let reader = new FileReader();
  37. //console.log(input);
  38. reader.readAsArrayBuffer(input.data);
  39. reader.onload = function () {
  40. //console.log(reader.result);
  41. let temp = curBlob;
  42. temp[input.id] = reader.result;
  43. setCurBlob(temp);
  44. };
  45. }
  46. export function convertXmlToObject(xmlArray) {
  47. let output = [];
  48. for (let i = 0; i < xmlArray.length; i++) {
  49. let tempObj = {};
  50. const keys = Object.keys(xmlArray[i]);
  51. const values = Object.values(xmlArray[i]);
  52. tempObj['id'] = i;
  53. tempObj['key'] = i;
  54. let userType = '';
  55. let userFirstName = '';
  56. let userLastName = '';
  57. //let userNameZh = "";
  58. for (let j = 0; j < Object.keys(xmlArray[i]).length; j++) {
  59. if (values[j][0] === 'NULL') {
  60. tempObj[keys[j]] = null;
  61. } else {
  62. tempObj[keys[j]] = values[j][0];
  63. }
  64. // if(keys[j] === "EMSDknownas"){
  65. // userNameZh = values[j][0];
  66. // }
  67. if (keys[j] === 'EMSDlotus') {
  68. userType = values[j][0];
  69. }
  70. if (keys[j] === 'EMSDlastname') {
  71. userLastName = values[j][0];
  72. }
  73. if (keys[j] === 'EMSDfirstname') {
  74. userFirstName = values[j][0];
  75. }
  76. }
  77. let displayLabel = userFirstName + ' ' + userLastName + ' (' + userType + ')';
  78. tempObj['label'] = displayLabel;
  79. if(displayLabel.trim() !== "()" && displayLabel.trim().length > 3){
  80. output.push(tempObj);
  81. }
  82. }
  83. return output;
  84. }
  85. //Get user added record by compare with the original data
  86. export function getDeletedRecordWithRefList(referenceList, updatedList) {
  87. return referenceList.filter((x) => !updatedList.includes(x));
  88. }
  89. //Get user deleted record by compare with the original data
  90. export function getNewRecordWithRefList(referenceList, updatedList) {
  91. return updatedList.filter((x) => !referenceList.includes(x));
  92. }
  93. //Get single combo value by using of label
  94. export function getComboValueByLabel(comboList, input) {
  95. for (let i = 0; i < comboList.length; i++) {
  96. if (comboList[i].label === input) {
  97. return comboList[i];
  98. }
  99. }
  100. return (input === undefined || input === "") ? null : input;
  101. }
  102. export function getComboValueById(comboList, input) {
  103. for (let i = 0; i < comboList.length; i++) {
  104. if (comboList[i].id === input) {
  105. return comboList[i];
  106. }
  107. }
  108. return input === undefined ? null : input;
  109. }
  110. export const getRowHeight = (params, fieldName, lengthToSplit) => {
  111. let temp = params.model[fieldName];
  112. //console.log(temp);
  113. if(!isObjEmpty(temp)){
  114. const SINGLE_LINE_HEIGHT = 40;
  115. const HEIGHT_WITH_PADDING = 35;
  116. const LINE_HEIGHT = 25; // Adjust this value based on your font size and row padding
  117. const numberOfLines = Math.ceil(temp.length / lengthToSplit); // Change 50 to your desired line length
  118. const height = numberOfLines < 2 ? SINGLE_LINE_HEIGHT : HEIGHT_WITH_PADDING*2 + (LINE_HEIGHT * (numberOfLines-2) );
  119. //console.log(height);
  120. return height <= SINGLE_LINE_HEIGHT ? SINGLE_LINE_HEIGHT : height;
  121. }
  122. else {
  123. return 40;
  124. }
  125. };
  126. //Get multi combo value by using of id
  127. export function getComboValueByIdList(comboList, idList) {
  128. if (!isObjEmpty(idList) && idList !== undefined) {
  129. const output = idList.map(function (id) {
  130. for (let i = 0; i < comboList.length; i++) {
  131. if (comboList[i].id === id) {
  132. return comboList[i];
  133. }
  134. }
  135. })
  136. .filter(Boolean); // Filter out undefined values from the output array
  137. return output;
  138. }
  139. return [];
  140. }
  141. //Check if user subDivisionId in List
  142. export function isUserDivisionIdInList(input, idList) {
  143. if (idList.length < 1) {
  144. return false;
  145. }
  146. for (let i = 0; i < idList.length; i++) {
  147. if (idList[i] === parseInt(input)) {
  148. return true;
  149. }
  150. }
  151. return false;
  152. }
  153. //Format List to String by using comma
  154. export function castListToString(input) {
  155. let output = '';
  156. for (let i = 0; i < input.length; i++) {
  157. if (i !== 0) {
  158. output = output + ',';
  159. }
  160. output = output + input[i];
  161. }
  162. return output;
  163. }
  164. //Convert Object list to id list
  165. export function getIdList(input) {
  166. const output = input.map(function (obj) {
  167. return obj.id;
  168. });
  169. return output;
  170. }
  171. //Convert Object list to JSON Object id List
  172. export function getJSONIdList(data) {
  173. return data.map(({ id }) => ({ id }));
  174. }
  175. //Convert Object list to field list
  176. export function getListByFieldName(input, name) {
  177. const output = input.map(function (obj) {
  178. return obj[name];
  179. });
  180. return output;
  181. }
  182. export function isStringEmptyAfterTrim(data){
  183. return (!data || data.trim().length === 0) ;
  184. }
  185. export function isFormEmpty(data){
  186. return (Object.values(data).every(value => !value)) ;
  187. }
  188. export function trimListDataBeforePost(dataList) {
  189. return dataList.map((dataObject) => {
  190. for (let key in dataObject) {
  191. if (typeof dataObject[key] === 'string') {
  192. dataObject[key] = dataObject[key].trim();
  193. }
  194. }
  195. return dataObject;
  196. });
  197. }
  198. export function trimDataBeforePost(dataObject){
  199. for (let key in dataObject) {
  200. if (typeof dataObject[key] === 'string') {
  201. dataObject[key] = dataObject[key].trim();
  202. }
  203. }
  204. return dataObject;
  205. }
  206. export function getObjectByValueName(list, valueName, value) {
  207. const obj = list.find((element) => {
  208. return element[valueName] === value;
  209. });
  210. return obj === undefined || Object.keys(obj).length <= 0 ? null : obj;
  211. }
  212. export function getObjectByName(list, name) {
  213. const obj = list.find((element) => {
  214. return element.name === name;
  215. });
  216. return obj === undefined || Object.keys(obj).length <= 0 ? null : obj;
  217. }
  218. export function getObjectById(list, id) {
  219. const obj = list.find((element) => {
  220. return element.id === id;
  221. });
  222. return obj === undefined || Object.keys(obj).length <= 0 ? null : obj;
  223. }
  224. export function removeObjectWithId(arr, id) {
  225. const arrCopy = Array.from(arr);
  226. const objWithIdIndex = arrCopy.findIndex((obj) => obj.id === id);
  227. arrCopy.splice(objWithIdIndex, 1);
  228. return arrCopy;
  229. }
  230. /**
  231. * Display the date array in string format, default format is 'yyyy-MM-dd'.
  232. *
  233. * @param {*} dateInput - The date value to be displayed, can be array or timestamp.
  234. * @param {*} [formatOption] - Set to True to display also the timestamp, or input custom format text.
  235. */
  236. export function getDateString(dateInput, formatOption=false) {
  237. if (dateInput === null || dateInput === undefined) {
  238. return null;
  239. }
  240. let dateFormat = 'yyyy-MM-dd';
  241. if (formatOption==true) {
  242. dateFormat = 'yyyy-MM-dd HH:mm:ss';
  243. } else if (typeof(formatOption) == 'string') {
  244. dateFormat = formatOption;
  245. }
  246. let inputType = 0; // Array
  247. if (!Array.isArray(dateInput)) {
  248. inputType = 1;
  249. }
  250. let date = new Date();
  251. if (inputType == 0) {
  252. let queryDateArray = dateInput;
  253. date = new Date(
  254. queryDateArray[0],
  255. queryDateArray[1]-1,
  256. queryDateArray[2],
  257. queryDateArray[3] !== undefined ? queryDateArray[3] : 0,
  258. queryDateArray[4] !== undefined ? queryDateArray[4] : 0,
  259. queryDateArray[5] !== undefined ? queryDateArray[5] : 0
  260. );
  261. } else {
  262. date = new Date(dateInput);
  263. }
  264. return format(date, dateFormat);
  265. }
  266. export const isOptionEqualToValue = (option, value) => {
  267. return option.id === value?.id;
  268. };
  269. export const dateComparator = (date1, date2) => {
  270. const dateString1 = getDateString(date1,false);
  271. const dateString2 = getDateString(date2,false);
  272. const date1Obj = new Date(dateString1);
  273. const date2Obj = new Date(dateString2);
  274. if (date1Obj < date2Obj) {
  275. return -1;
  276. }
  277. if (date1Obj > date2Obj) {
  278. return 1;
  279. }
  280. return 0;
  281. };
  282. //====================COMMON NOTIFICATION====================//
  283. export const notifySaveSuccess = () =>
  284. toast.success('Save success!', {
  285. position: 'bottom-left',
  286. autoClose: 5000,
  287. hideProgressBar: false,
  288. closeOnClick: true,
  289. pauseOnHover: true,
  290. draggable: true,
  291. progress: undefined,
  292. theme: 'light'
  293. });
  294. export const notifySendSuccess = () =>
  295. toast.success('Send success!', {
  296. position: 'bottom-left',
  297. autoClose: 5000,
  298. hideProgressBar: false,
  299. closeOnClick: true,
  300. pauseOnHover: true,
  301. draggable: true,
  302. progress: undefined,
  303. theme: 'light'
  304. });
  305. export const notifyLoadSuccess = () =>
  306. toast.success('Load success!', {
  307. position: 'bottom-left',
  308. autoClose: 5000,
  309. hideProgressBar: false,
  310. closeOnClick: true,
  311. pauseOnHover: true,
  312. draggable: true,
  313. progress: undefined,
  314. theme: 'light'
  315. });
  316. export const notifyDeleteSuccess = () =>
  317. toast.success('Delete Success!', {
  318. position: 'bottom-left',
  319. autoClose: 5000,
  320. hideProgressBar: false,
  321. closeOnClick: true,
  322. pauseOnHover: true,
  323. draggable: true,
  324. progress: undefined,
  325. theme: 'light'
  326. });
  327. export const notifyInfo = (infoString) =>
  328. toast.info(infoString, {
  329. position: 'bottom-left',
  330. autoClose: 5000,
  331. hideProgressBar: false,
  332. closeOnClick: true,
  333. pauseOnHover: true,
  334. draggable: true,
  335. progress: undefined,
  336. theme: 'light'
  337. });
  338. export const notifySuccess = (infoString) =>
  339. toast.success(infoString, {
  340. position: 'bottom-left',
  341. autoClose: 5000,
  342. hideProgressBar: false,
  343. closeOnClick: true,
  344. pauseOnHover: true,
  345. draggable: true,
  346. progress: undefined,
  347. theme: 'light'
  348. });
  349. export const notifyDeleteError = (errorString) =>
  350. toast.error(errorString, {
  351. position: 'bottom-left',
  352. autoClose: 5000,
  353. hideProgressBar: false,
  354. closeOnClick: true,
  355. pauseOnHover: true,
  356. draggable: true,
  357. progress: undefined,
  358. theme: 'light'
  359. });
  360. export const notifyNoPermission = () =>
  361. toast.error('You do not have permission to access this page', {
  362. position: 'bottom-left',
  363. autoClose: 5000,
  364. hideProgressBar: false,
  365. closeOnClick: true,
  366. pauseOnHover: true,
  367. draggable: true,
  368. progress: undefined,
  369. theme: 'light'
  370. });
  371. //====================COMMON COMPONENT====================//
  372. export function CustomNoRowsOverlay() {
  373. return (
  374. <GridOverlay>
  375. <Typography variant="body1">No record found</Typography>
  376. </GridOverlay>
  377. );
  378. }
  379. export function GeneralConfirmWindow({ isWindowOpen, title, content, onNormalClose, onConfirmClose }) {
  380. return (
  381. <Dialog
  382. open={isWindowOpen}
  383. onClose={onNormalClose}
  384. aria-labelledby="alert-dialog-title"
  385. aria-describedby="alert-dialog-description"
  386. PaperProps={{
  387. sx: {
  388. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '30vw' },
  389. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  390. }
  391. }}
  392. >
  393. <DialogTitle id="alert-dialog-title">
  394. <Typography variant="lionerBold" component="span" color={GENERAL_RED_COLOR}>
  395. {title}
  396. </Typography>
  397. </DialogTitle>
  398. <DialogContent>
  399. <DialogContentText id="alert-dialog-description">
  400. <Typography variant="lionerSize" component="span" color={GENERAL_TEXT_COLOR}>
  401. {content}
  402. </Typography>
  403. </DialogContentText>
  404. </DialogContent>
  405. <DialogActions>
  406. <Button onClick={onNormalClose}>
  407. <Typography variant="lionerSize" component="span">
  408. Cancel
  409. </Typography>
  410. </Button>
  411. <Button onClick={onConfirmClose} autoFocus>
  412. <Typography variant="lionerSize" component="span">
  413. Confirm
  414. </Typography>
  415. </Button>
  416. </DialogActions>
  417. </Dialog>
  418. );
  419. }
  420. export function GeneralComboWindow({ isWindowOpen, title, content, comboList, setValueCallback, onNormalClose, onConfirmClose }) {
  421. const [errors, setErrors] = useState({});
  422. const [temp, setTemp] = useState(null);
  423. function validate() {
  424. const formErrors = {};
  425. if (temp == null) {
  426. formErrors.error = 'Event must not be null';
  427. }
  428. setErrors(formErrors);
  429. if (Object.keys(formErrors).length === 0) {
  430. onConfirmClose();
  431. }
  432. }
  433. return (
  434. <Dialog
  435. PaperProps={{
  436. sx: {
  437. minWidth: '40vw',
  438. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '50vw' },
  439. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  440. }
  441. }}
  442. open={isWindowOpen}
  443. onClose={onNormalClose}
  444. aria-labelledby="alert-dialog-title"
  445. aria-describedby="alert-dialog-description"
  446. >
  447. <DialogTitle id="alert-dialog-title">{title}</DialogTitle>
  448. <DialogContent>
  449. <Grid item xs={11} s={11} md={11} lg={11} sx={{ mt: 3, ml: 3, mr: 3, mb: 3 }}>
  450. <InputLabel>
  451. {content}
  452. <Typography sx={{ color: GENERAL_RED_COLOR }} component="span">
  453. *
  454. </Typography>
  455. </InputLabel>
  456. <Autocomplete
  457. id="confirm-combo"
  458. size="small"
  459. options={comboList}
  460. onChange={(event, newValue) => {
  461. setValueCallback(newValue);
  462. setTemp(newValue);
  463. }}
  464. renderInput={(params) => (
  465. <TextField
  466. {...params}
  467. //label={content}
  468. error={!!errors.error}
  469. helperText={errors.error}
  470. InputProps={{
  471. ...params.InputProps,
  472. style: {
  473. height: 'auto',
  474. overflow: 'hidden',
  475. paddingTop: '8px', // Adjust as needed for vertical padding
  476. paddingBottom: '8px', // Adjust as needed for vertical padding
  477. },
  478. classes: { input: 'autocomplete-input' },
  479. }}
  480. multiline
  481. maxRows={3}
  482. />
  483. )}
  484. />
  485. </Grid>
  486. </DialogContent>
  487. <DialogActions>
  488. <Button onClick={onNormalClose}>Cancel</Button>
  489. <Button onClick={validate} autoFocus>
  490. Confirm
  491. </Button>
  492. </DialogActions>
  493. </Dialog>
  494. );
  495. }
  496. export function GeneralTwoComboWindow({
  497. isWindowOpen,
  498. title,
  499. content,
  500. content2,
  501. comboList,
  502. comboList2,
  503. setValueCallback,
  504. setValueCallback2,
  505. onNormalClose,
  506. onConfirmClose
  507. }) {
  508. const [errors, setErrors] = useState({});
  509. const [temp, setTemp] = useState(null);
  510. const [temp2, setTemp2] = useState(null);
  511. function validate() {
  512. const formErrors = {};
  513. if (temp === null) {
  514. formErrors.error = 'Event must not be null';
  515. }
  516. if (temp2 === null) {
  517. formErrors.error2 = 'Application must not be null';
  518. }
  519. setErrors(formErrors);
  520. if (Object.keys(formErrors).length === 0) {
  521. onConfirmClose();
  522. }
  523. }
  524. useEffect(() => {
  525. setValueCallback2(null);
  526. setTemp2(null);
  527. }, [temp]);
  528. return (
  529. <React.Fragment>
  530. <Dialog
  531. PaperProps={{
  532. sx: {
  533. minWidth: '40vw',
  534. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '50vw' },
  535. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  536. }
  537. }}
  538. open={isWindowOpen}
  539. onClose={onNormalClose}
  540. aria-labelledby="alert-dialog-title"
  541. aria-describedby="alert-dialog-description"
  542. >
  543. <DialogTitle id="alert-dialog-title">{title}</DialogTitle>
  544. <DialogContent>
  545. <Grid item xs={11} s={11} md={11} lg={11} sx={{ mt: 3, ml: 3, mr: 3, mb: 3 }}>
  546. <InputLabel>
  547. {content}
  548. <Typography sx={{ color: GENERAL_RED_COLOR }} component="span">
  549. *
  550. </Typography>
  551. </InputLabel>
  552. <Autocomplete
  553. id="confirm-combo"
  554. size="small"
  555. options={comboList}
  556. value={temp === null? null : temp}
  557. onChange={(event, newValue) => {
  558. setValueCallback(newValue);
  559. setTemp(newValue);
  560. }}
  561. renderInput={(params) => (
  562. <TextField
  563. {...params}
  564. //label={content}
  565. error={!!errors.error}
  566. helperText={errors.error}
  567. InputProps={{
  568. ...params.InputProps,
  569. style: {
  570. height: 'auto',
  571. overflow: 'hidden',
  572. paddingTop: '8px', // Adjust as needed for vertical padding
  573. paddingBottom: '8px', // Adjust as needed for vertical padding
  574. },
  575. classes: { input: 'autocomplete-input' },
  576. }}
  577. multiline
  578. maxRows={3}
  579. />
  580. )}
  581. />
  582. </Grid>
  583. <Grid item xs={11} s={11} md={11} lg={11} sx={{ mt: 3, ml: 3, mr: 3, mb: 3 }}>
  584. <InputLabel>
  585. {content2}
  586. <Typography sx={{ color: GENERAL_RED_COLOR }} component="span">
  587. *
  588. </Typography>
  589. </InputLabel>
  590. <Autocomplete
  591. id="application-combo"
  592. size="small"
  593. options={comboList2}
  594. onChange={(event, newValue) => {
  595. setValueCallback2(newValue);
  596. setTemp2(newValue);
  597. }}
  598. value={temp2 === null? null : temp2}
  599. disabled={temp === null}
  600. renderInput={(params) => (
  601. <TextField
  602. {...params}
  603. //label={content2}
  604. error={!!errors.error2}
  605. helperText={errors.error2}
  606. InputProps={{
  607. ...params.InputProps,
  608. style: {
  609. height: 'auto',
  610. overflow: 'hidden',
  611. paddingTop: '8px', // Adjust as needed for vertical padding
  612. paddingBottom: '8px', // Adjust as needed for vertical padding
  613. },
  614. classes: { input: 'autocomplete-input' },
  615. }}
  616. multiline
  617. maxRows={3}
  618. />
  619. )}
  620. />
  621. </Grid>
  622. </DialogContent>
  623. <DialogActions>
  624. <Button onClick={onNormalClose}>Cancel</Button>
  625. <Button onClick={validate} autoFocus>
  626. Confirm
  627. </Button>
  628. </DialogActions>
  629. </Dialog>
  630. </React.Fragment>
  631. );
  632. }
  633. export function GeneralCreateTemplateWindow({
  634. isWindowOpen,
  635. title,
  636. content,
  637. setValueCallback,
  638. validatePath,
  639. onNormalClose,
  640. onConfirmClose,
  641. module
  642. }) {
  643. const [errors, setErrors] = useState({});
  644. const [temp, setTemp] = useState(null);
  645. function validateTemplateName() {
  646. const userData = getUserData();
  647. axios
  648. .get(`${apiPath}${validatePath}`, {
  649. params: {
  650. name: temp.trim(),
  651. userId: userData.id,
  652. module: module
  653. }
  654. })
  655. .then((response) => {
  656. if (response.status === 200) {
  657. const formErrors = {};
  658. if (response.data.isTaken) {
  659. formErrors.error = 'Name have been taken';
  660. setErrors(formErrors);
  661. } else if (!response.data.isTaken) {
  662. if (Object.keys(formErrors).length === 0) {
  663. onConfirmClose();
  664. }
  665. }
  666. }
  667. })
  668. .catch((error) => {
  669. console.log(error);
  670. return true;
  671. });
  672. }
  673. function validate() {
  674. const formErrors = {};
  675. if (temp === null) {
  676. formErrors.error = 'Template name must not be null';
  677. }
  678. else if (temp.trim().length === 0){
  679. formErrors.error = 'Template name must not be null';
  680. }
  681. setErrors(formErrors);
  682. if (Object.keys(formErrors).length === 0) {
  683. validateTemplateName();
  684. }
  685. }
  686. const onInputChange = (event) => {
  687. setTemp(event.target.value);
  688. setValueCallback(event.target.value);
  689. };
  690. return (
  691. <Dialog
  692. PaperProps={{
  693. sx: {
  694. minWidth: '25vw',
  695. maxWidth: { xs: '90vw', s: '90vw', m: '70vw', lg: '50vw' },
  696. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '50vh' }
  697. }
  698. }}
  699. open={isWindowOpen}
  700. onClose={onNormalClose}
  701. aria-labelledby="alert-dialog-title"
  702. aria-describedby="alert-dialog-description"
  703. >
  704. <DialogTitle id="alert-dialog-title">{title}</DialogTitle>
  705. <DialogContent>
  706. <Grid item xs={9} s={9} md={9} lg={9} sx={{ mt: 3, ml: 3, mr: 3, mb: 3 }}>
  707. <InputLabel>
  708. {content}
  709. <Typography sx={{ color: GENERAL_RED_COLOR }} component="span">
  710. *
  711. </Typography>
  712. </InputLabel>
  713. <TextField fullWidth onChange={onInputChange} id="templateName" size="small" error={!!errors.error} helperText={errors.error} />
  714. </Grid>
  715. </DialogContent>
  716. <DialogActions>
  717. <Button onClick={onNormalClose}>Cancel</Button>
  718. <Button onClick={validate} autoFocus>
  719. Confirm
  720. </Button>
  721. </DialogActions>
  722. </Dialog>
  723. );
  724. }
  725. export function UploadFileWindow({ isWindowOpen, title, video, onNormalClose, onConfirmClose }) {
  726. const [errors, setErrors] = useState({});
  727. const [file, setFile] = React.useState(null)
  728. const fileInputRef = useRef(null);
  729. const { register, getValues, setValue } = useForm();
  730. useEffect(() => {
  731. setValue('remarks', video.remarks);
  732. }, [video]);
  733. const handleOnClose = () => {
  734. setFile(null);
  735. onNormalClose();
  736. };
  737. const handleFieldChange = (newFile) => {
  738. setFile(newFile);
  739. };
  740. const handleChange = (event) => {
  741. if(event !== null){
  742. const newFile = event.target.files[0];
  743. setFile(newFile);
  744. }
  745. else{
  746. setFile(null)
  747. }
  748. };
  749. const handleOpenFileSelect = () => {
  750. fileInputRef.current.click();
  751. };
  752. function validate() {
  753. const formErrors = {};
  754. /*if (file == null) {
  755. formErrors.error = 'File must not be null';
  756. }*/
  757. if (file !== null) {
  758. if (file.type.split('/')[0] !== 'image') {
  759. formErrors.error = 'File must be a image';
  760. }
  761. }
  762. setErrors(formErrors);
  763. if (Object.keys(formErrors).length === 0) {
  764. uploadData();
  765. }
  766. }
  767. function uploadData() {
  768. const values = getValues();
  769. let formData = new FormData();
  770. let containData = false;
  771. formData.append('fileId', video.id);
  772. if (file !== null) {
  773. formData.append('file', file);
  774. containData = true;
  775. }
  776. if (values.remarks) {
  777. formData.append('remarks', values.remarks);
  778. containData = true;
  779. }
  780. if (containData) {
  781. axios
  782. .post(`${apiPath}${POST_THUMBNAIL_PATH}`, formData, {
  783. headers: {
  784. 'Content-Type': 'multipart/form-data'
  785. }
  786. })
  787. .then((response) => {
  788. if (response.status === 200) {
  789. notifySaveSuccess();
  790. setFile(null);
  791. onConfirmClose();
  792. }
  793. })
  794. .catch((error) => {
  795. console.log(error);
  796. return false;
  797. });
  798. }
  799. }
  800. return (
  801. <Dialog
  802. PaperProps={{
  803. sx: {
  804. minWidth: '50vw',
  805. width: { xs: '90vw', s: '90vw', m: '70vw', lg: '50vw' },
  806. maxHeight: { xs: '90vh', s: '70vh', m: '70vh', lg: '70vh' }
  807. }
  808. }}
  809. open={isWindowOpen}
  810. onClose={onNormalClose}
  811. aria-labelledby="alert-dialog-title"
  812. aria-describedby="alert-dialog-description"
  813. >
  814. <DialogTitle id="alert-dialog-title">
  815. <Typography variant="lionerBold" component="span">
  816. {title}
  817. </Typography>
  818. </DialogTitle>
  819. <DialogContent>
  820. <Grid container>
  821. <Grid item xs={12} s={12} md={12} lg={12} sx={{ ml: 3, mr: 3, mb: 1 }}>
  822. <Grid container alignItems={'center'}>
  823. <Grid item xs={4} s={4} md={4} lg={4} sx={{ ml: 1, mr: 3, display: 'flex', alignItems: 'center' }}>
  824. <Typography variant="lionerLabel" component="span">
  825. File Name:
  826. </Typography>
  827. </Grid>
  828. <Grid item xs={7} s={7} md={7} lg={7}>
  829. <FormControl fullWidth required>
  830. <Typography variant="lionerLabel" component="span">
  831. {video.key}
  832. </Typography>
  833. </FormControl>
  834. </Grid>
  835. </Grid>
  836. {video.mimetype === 'video' ? (
  837. <Grid container alignItems={'center'} sx={{ mt: 3 }}>
  838. <Grid item xs={4} s={4} md={4} lg={4} sx={{ ml: 1, mr: 3, display: 'flex', alignItems: 'center' }}>
  839. <Typography variant="lionerLabel" component="span">
  840. Thumbnail:
  841. </Typography>
  842. </Grid>
  843. <Grid item xs={7} s={7} md={7} lg={7}>
  844. <FormControl fullWidth required>
  845. <Grid container alignItems={'flex-start'} sx={{ mt: 3 }}>
  846. <Grid item xs={7} s={7} md={7} lg={8} sx={{display: 'flex', alignItems: 'flex-start' }}>
  847. <MuiFileInput
  848. inputProps={{
  849. accept: '.png, .jpeg, .jpg',
  850. }}
  851. InputProps={{
  852. padding: '7.5px 8px 7.5px 12px',
  853. startAdornment: null, // Set startAdornment to null to disable it
  854. }}
  855. value={file}
  856. onChange={handleFieldChange}
  857. error={!!errors.error}
  858. helperText={errors.error}
  859. disabled={video.mimetype !== 'video'}
  860. />
  861. </Grid>
  862. <Grid item xs={4} s={4} md={4} lg={4} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
  863. <div>
  864. <input
  865. type="file"
  866. accept=".png, .jpeg, .jpg"
  867. style={{ display: 'none' }}
  868. ref={fileInputRef}
  869. onChange={handleChange}
  870. />
  871. <Button
  872. component="label"
  873. variant="contained"
  874. startIcon={<CloudUploadIcon />}
  875. inputprops={{
  876. accept: '.png, .jpeg, .jpg',
  877. startAdornment: <AttachFileIcon />
  878. }}
  879. onClick={handleOpenFileSelect}
  880. >
  881. Upload file
  882. </Button>
  883. {/*Selected file: {file ? file.name : 'None'}*/}
  884. </div>
  885. </Grid>
  886. </Grid>
  887. </FormControl>
  888. </Grid>
  889. </Grid>
  890. ) : (
  891. <Grid />
  892. )}
  893. <Grid container alignItems={'flex-start'} sx={{ mt: 3 }}>
  894. <Grid item xs={4} s={4} md={4} lg={4} sx={{ ml: 1, mr: 3, display: 'flex', alignItems: 'flex-start' }}>
  895. <Typography variant="lionerLabel" component="span">
  896. Remark:
  897. </Typography>
  898. </Grid>
  899. <Grid item xs={7} s={7} md={7} lg={7}>
  900. <TextField
  901. fullWidth
  902. inputProps={{maxLength: 255, style: {fontSize: '1.1rem'}}}
  903. {...register('remarks', {
  904. value: video.remarks
  905. })}
  906. id="remarks"
  907. InputProps={{
  908. style: { minHeight:'42.5px', maxHeight: '50vh' },
  909. }}
  910. multiline
  911. rows={3}
  912. />
  913. </Grid>
  914. </Grid>
  915. </Grid>
  916. </Grid>
  917. </DialogContent>
  918. <DialogActions>
  919. <Button onClick={handleOnClose}>
  920. <Typography variant="lionerLabel" component="span">
  921. Cancel
  922. </Typography>
  923. </Button>
  924. <Button onClick={validate} autoFocus>
  925. <Typography variant="lionerLabel" component="span">
  926. Confirm
  927. </Typography>
  928. </Button>
  929. </DialogActions>
  930. </Dialog>
  931. );
  932. }