([]);\n\n useEffect(() => {\n getCountries().then(result => {\n setCountries(result.data);\n });\n }, []);\n\n return isEmpty(props.countryIds)\n ? {t('country-selection-ui.no-country-permissions')}
\n : \n Country \n {\n if (props.selectedCountryId.length === 0) {\n return {t('country-selection-ui.select-default')} ;\n }\n return countries.find(c => c.countryId === props.selectedCountryId)?.name;\n }}\n >\n \n {t('country-selection-ui.select-default')} \n \n\n {props.countryIds.map(countryId => ({\n countryId,\n name: countries.find(c => c.countryId === countryId)?.name ?? countryId ?? ''\n })).sort((a, b) => a.name.localeCompare(b.name)).map(c => (\n \n {c.name}\n \n ))}\n \n
;\n}\n","import { InputLabel, MenuItem, Select } from '@material-ui/core';\nimport React from 'react';\nimport { Trans, useTranslation } from 'react-i18next';\n\nimport { ConstructionType } from '../../types/domain/construction-data.types';\n\nexport type ConstructionTypeSelectionPropType = {\n constructionTypes: ConstructionType[];\n selectedConstructionTypeId: string;\n disabled: boolean;\n handleChange: (event: React.ChangeEvent<{ value: unknown }>) => void\n};\n\nexport default function ConstructionTypeSelection(props: ConstructionTypeSelectionPropType) {\n\n const { t } = useTranslation();\n\n return (\n \n \n Construction Type \n \n {\n if (props.selectedConstructionTypeId.length === 0) {\n return {t('construction-type-selection-ui.select-default')} ;\n }\n return props.constructionTypes.find(ct => ct.id === props.selectedConstructionTypeId)?.constructionTypeNameLocalized;\n }}\n >\n \n {t('construction-type-selection-ui.select-default')} \n \n\n {props.constructionTypes.map((ct: ConstructionType) => (\n \n {ct.constructionTypeNameLocalized}\n \n ))}\n \n
\n );\n}\n","import { StoreModel } from \"../store.model\";\n\nexport const countryIdSelector = (state: StoreModel): string => state.masterDataDownload.countryId;\n\nexport const constructionTypeIdSelector = (state: StoreModel): string => state.masterDataDownload.constructionTypeId;\n\nexport const errorSelector = (state: StoreModel): any => state.masterDataDownload.error;\n\nexport const isLoadingSelector = (state: StoreModel): boolean => state.masterDataDownload.isLoading;\n","import {\n SetCountryId,\n SET_COUNTRY_ID,\n SetConstructionTypeId,\n SET_CONSTRUCTION_TYPE_ID,\n SetError,\n SET_ERROR,\n DownloadMasterDataTemplateAttempt,\n DOWNLOAD_MASTER_DATA_TEMPLATE_ATTEMPT,\n DownloadMasterDataTemplateSuccess,\n DOWNLOAD_MASTER_DATA_TEMPLATE_SUCCESS,\n DownloadMasterDataTemplateFailure,\n DOWNLOAD_MASTER_DATA_TEMPLATE_FAILURE\n} from \"./master-data-download.action-types\";\n\nexport const setCountryId = (countryId: string): SetCountryId => ({\n type: SET_COUNTRY_ID,\n payload: countryId,\n});\n\nexport const setConstructionTypeId = (constructionTypeId: string): SetConstructionTypeId => ({\n type: SET_CONSTRUCTION_TYPE_ID,\n payload: constructionTypeId,\n});\n\nexport const setError = (error: any): SetError => ({\n type: SET_ERROR,\n error: error,\n});\n\nexport const downloadMasterDataTemplateAttempt = (): DownloadMasterDataTemplateAttempt => ({\n type: DOWNLOAD_MASTER_DATA_TEMPLATE_ATTEMPT,\n});\n\nexport const downloadMasterDataTemplateSuccess = (): DownloadMasterDataTemplateSuccess => ({\n type: DOWNLOAD_MASTER_DATA_TEMPLATE_SUCCESS\n});\n\nexport const downloadMasterDataTemplateFailure = (error: any): DownloadMasterDataTemplateFailure => ({\n type: DOWNLOAD_MASTER_DATA_TEMPLATE_FAILURE,\n error: error,\n});\n","\nimport { StoreModel } from '../store.model';\nimport { ConstructionType } from '../../types/domain/construction-data.types';\n\nexport const constructionTypesSelector = (state: StoreModel): ConstructionType[] => state.constructionData.constructionTypes;\n","import axios from 'axios';\nimport { getAuthProvider } from '../../authentication/authProvider';\nimport {\n fetchConstructionTypesAttempt,\n fetchConstructionTypesSuccess,\n fetchConstructionTypesFailure\n} from './construction-data.actions';\n\nexport const fetchConstructionTypes = (countryId: string) => (\n\n // TODO: Typing needs to be defined\n async (dispatch: (action: any) => void) => {\n dispatch(fetchConstructionTypesAttempt());\n\n try {\n const token = await getAuthProvider().getIdToken();\n const { data } = await axios.get('/api/constructionTypes/' + countryId, {\n headers: { 'Authorization': `Bearer ${token.idToken.rawIdToken}` }\n });\n\n // TODO: We might want to group the construction types by category and save it that way to the store\n // Here is the code but it's not in use just yet\n // _____________________\n // const constructionTypesByCategory = groupBy(data, (constructionType: ConstructionType) => constructionType.category);\n // console.log('ConstructionTypes grouped by category', constructionTypesByCategory);\n\n dispatch(fetchConstructionTypesSuccess(data));\n } catch (error) {\n dispatch(fetchConstructionTypesFailure(error));\n }\n }\n);\n","import {\n FETCH_CONSTRUCTION_TYPES_ATTEMPT,\n FETCH_CONSTRUCTION_TYPES_SUCCESS,\n FETCH_CONSTRUCTION_TYPES_FAILURE,\n FetchConstructionTypesAttempt,\n FetchConstructionTypesSuccess,\n FetchConstructionTypesFailure,\n} from './construction-data.action-types';\nimport { ConstructionType } from '../../types/domain/construction-data.types';\n\nexport const fetchConstructionTypesAttempt = (): FetchConstructionTypesAttempt => ({\n type: FETCH_CONSTRUCTION_TYPES_ATTEMPT,\n});\n\nexport const fetchConstructionTypesSuccess = (data: ConstructionType[]): FetchConstructionTypesSuccess => ({\n type: FETCH_CONSTRUCTION_TYPES_SUCCESS,\n payload: data,\n});\n\nexport const fetchConstructionTypesFailure = (error: any): FetchConstructionTypesFailure => ({\n type: FETCH_CONSTRUCTION_TYPES_FAILURE,\n error,\n});\n","import { isEmpty } from 'lodash';\nimport axios from 'axios';\nimport { getAuthProvider } from '../../authentication/authProvider';\nimport {\n setError,\n downloadMasterDataTemplateAttempt,\n downloadMasterDataTemplateFailure,\n downloadMasterDataTemplateSuccess,\n} from './master-data-download.actions';\n\nexport const downloadMasterDataTemplate = (countryId: string, constructionTypeId: string) => (\n async (dispatch: any) => {\n if (isEmpty(countryId) || isEmpty(constructionTypeId)) {\n dispatch(setError({ message: 'Please select country and construction type before downloading' }));\n return;\n }\n\n dispatch(downloadMasterDataTemplateAttempt());\n\n try {\n const token = await getAuthProvider().getIdToken();\n const { data } = await axios.get(`api/MasterData/${constructionTypeId}/${countryId}`, {\n headers: { 'Authorization': `Bearer ${token.idToken.rawIdToken}` },\n responseType: 'blob'\n });\n\n // Creating link to the Blob\n const url = window.URL.createObjectURL(new Blob([data]));\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `Materials-${countryId.toUpperCase()}-${constructionTypeId}.xlsx`);\n document.body.appendChild(link);\n // @ts-ignore\n if (window.Cypress) {\n // NOTE: needed for Automated tests, do not remove\n return;\n }\n link.click();\n link.parentNode?.removeChild(link);\n\n dispatch(downloadMasterDataTemplateSuccess());\n } catch(error) {\n dispatch(downloadMasterDataTemplateFailure(error));\n }\n\n }\n)\n","import React, { useState } from 'react';\nimport { useSelector } from 'react-redux';\nimport { countryIdsSelector, hasTechnicalRoleSelector } from '../../store/user/user.selectors';\nimport { Button, Box, CircularProgress, Divider } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { isEmpty } from 'lodash';\nimport axios from 'axios';\nimport { toast } from 'react-toastify';\nimport { getAuthProvider } from '../../authentication/authProvider';\n\nexport default function MasterDataDownloadAll() {\n\n const countryIds = useSelector(countryIdsSelector);\n const hasTechnicalRole = useSelector(hasTechnicalRoleSelector);\n const [isDownloadingAll, setDownloadingAll] = useState(false);\n const [downloadAllError, setDownloadAllError] = useState('');\n\n const handleDownloadAllClick = async () => {\n setDownloadingAll(true);\n setDownloadAllError('');\n\n var startTimeMs = new Date().getTime();\n\n try {\n const token = await getAuthProvider().getIdToken();\n const { data } = await axios.get('api/MasterData/zip-all', {\n headers: { 'Authorization': `Bearer ${token.idToken.rawIdToken}` },\n responseType: 'blob'\n });\n\n var endTimeMs = new Date().getTime();\n var durationMs = endTimeMs - startTimeMs;\n var durationSeconds1dp = Math.round(durationMs / 100) / 10;\n toast.success(`Downloaded Materials Master Data for countries ${countryIds.join(', ')} in ${durationSeconds1dp} seconds.`);\n\n // Creating link to the Blob\n const url = window.URL.createObjectURL(new Blob([data]));\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `Materials-All-${countryIds.join('-')}.zip`);\n document.body.appendChild(link);\n link.click();\n link.parentNode?.removeChild(link);\n\n setDownloadingAll(false);\n } catch(error) {\n setDownloadingAll(false);\n setDownloadAllError(error?.toString() || 'Unknown error');\n }\n }\n\n return (\n \n {countryIds.length > 0 && hasTechnicalRole &&
\n
\n
Download all master data \n
\n \n Download All \n {isDownloadingAll && }\n
\n \n Downloads a zip file containing the Master Data for all of the countries you have access to.\n \n You can access: {countryIds.join(', ')}\n \n \n {!isEmpty(downloadAllError) &&
{downloadAllError} }\n
}\n
\n );\n};\n","import React, { useEffect } from 'react';\nimport { useSelector, useDispatch } from 'react-redux';\nimport { Trans } from 'react-i18next';\nimport { hasTechnicalRoleSelector, countryIdsSelector } from '../../store/user/user.selectors';\nimport CountrySelection from '../CountrySelection/CountrySelection';\nimport ConstructionTypeSelection from '../ConstructionTypeSelection/ConstructionTypeSelection';\nimport {\n countryIdSelector,\n constructionTypeIdSelector,\n errorSelector,\n isLoadingSelector\n} from '../../store/master-data-download/master-data-download.selectors';\nimport { setCountryId, setConstructionTypeId } from '../../store/master-data-download/master-data-download.actions';\nimport { constructionTypesSelector } from '../../store/construction-data/construction-data.selectors';\nimport { fetchConstructionTypes } from '../../store/construction-data/construction-data.action-creators';\nimport { Button, Box, CircularProgress } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { downloadMasterDataTemplate } from '../../store/master-data-download/master-data-download.action-creators';\nimport { isEmpty } from 'lodash';\nimport MasterDataDownloadAll from './MasterDataDownloadAll';\n\nexport default function MasterDataDownload() {\n\n const dispatch = useDispatch();\n const error = useSelector(errorSelector);\n const isLoading = useSelector(isLoadingSelector);\n\n // All the things for CountrySelection\n const countryIds = useSelector(countryIdsSelector);\n const selectedCountryId = useSelector(countryIdSelector);\n const handleCountryChange = (event: React.ChangeEvent<{ value: unknown }>) => {\n dispatch(setConstructionTypeId(''));\n dispatch(setCountryId(event.target.value as string));\n };\n\n // All the things for ConstructionTypeSelection\n const constructionTypes = useSelector(constructionTypesSelector);\n const selectedConstructionTypeId = useSelector(constructionTypeIdSelector);\n const handleConstructionTypeChange = (event: React.ChangeEvent<{ value: unknown }>) => {\n dispatch(setConstructionTypeId(event.target.value as string));\n };\n\n const hasTechnicalRole = useSelector(hasTechnicalRoleSelector);\n\n const handleDownloadOnClick = () => {\n dispatch(downloadMasterDataTemplate(selectedCountryId, selectedConstructionTypeId));\n }\n\n useEffect(() => {\n if (selectedCountryId) {\n dispatch(fetchConstructionTypes(selectedCountryId));\n }\n }, [selectedCountryId, dispatch]);\n\n return (\n \n
download-master-data \n\n {!hasTechnicalRole ?
no-permission
\n :
\n
\n \n \n\n
\n 0 ? false : true}\n />\n \n\n {isLoading &&
}\n\n
\n Download \n \n\n {!isEmpty(error) &&
{error.message} }\n
}\n\n
\n
\n );\n};\n","import {\n UPLOAD_MASTER_DATA_ATTEMPT,\n UploadMasterDataAttempt,\n UPLOAD_MASTER_DATA_SUCCESS,\n UploadMasterDataSuccess,\n UploadMasterDataFailure,\n UPLOAD_MASTER_DATA_FAILURE,\n UploadMasterDataBadRequest,\n UPLOAD_MASTER_DATA_BAD_REQUEST,\n COMMIT_MASTER_DATA_ATTEMPT,\n CommitMasterDataAttempt,\n CommitMasterDataSuccess,\n COMMIT_MASTER_DATA_SUCCESS,\n CommitMasterDataFailure,\n COMMIT_MASTER_DATA_FAILURE\n} from './master-data-upload.action-types';\nimport { BadRequest, Ok } from '../../types/master-data-upload-response.types';\n\nexport const uploadMasterDataAttempt = (): UploadMasterDataAttempt => ({\n type: UPLOAD_MASTER_DATA_ATTEMPT,\n});\n\nexport const uploadMasterDataSuccess = (data: Ok): UploadMasterDataSuccess => ({\n type: UPLOAD_MASTER_DATA_SUCCESS,\n payload: data,\n});\n\nexport const uploadMasterDataBadRequest = (data: BadRequest): UploadMasterDataBadRequest => ({\n type: UPLOAD_MASTER_DATA_BAD_REQUEST,\n payload: data,\n});\n\nexport const uploadMasterDataFailure = (error: any): UploadMasterDataFailure => ({\n type: UPLOAD_MASTER_DATA_FAILURE,\n error: error,\n});\n\nexport const commitMasterDataAttempt = (): CommitMasterDataAttempt => ({\n type: COMMIT_MASTER_DATA_ATTEMPT,\n});\n\nexport const commitMasterDataSuccess = (): CommitMasterDataSuccess => ({\n type: COMMIT_MASTER_DATA_SUCCESS,\n});\n\nexport const commitMasterDataFailure = (error: any): CommitMasterDataFailure => ({\n type: COMMIT_MASTER_DATA_FAILURE,\n error: error,\n});\n","import {\n uploadMasterDataAttempt,\n uploadMasterDataFailure,\n uploadMasterDataSuccess,\n uploadMasterDataBadRequest,\n commitMasterDataAttempt,\n commitMasterDataSuccess,\n commitMasterDataFailure,\n} from './master-data-upload.actions';\nimport axios from 'axios';\nimport { getAuthProvider } from '../../authentication/authProvider';\nimport { ResponseCode } from '../../types/response-code.types';\n\nexport const uploadMasterData = (file: any, fileName: string, commit: boolean = false) => (\n async (dispatch: (action: any) => void) => {\n if (commit) {\n dispatch(commitMasterDataAttempt())\n } else {\n dispatch(uploadMasterDataAttempt());\n }\n\n try {\n const formData = new FormData();\n formData.append('masterDataFile', file as string, fileName);\n\n const token = await getAuthProvider().getIdToken();\n const { data } = await axios.post('api/masterData', formData, {\n headers: {\n 'Authorization': `Bearer ${token.idToken.rawIdToken}`,\n 'Content-Type': 'multipart/form-data'\n },\n params: {\n commit: commit,\n }\n });\n \n if (commit) {\n dispatch(commitMasterDataSuccess());\n } else {\n dispatch(uploadMasterDataSuccess({ ...data }));\n }\n\n } catch (error) {\n if (commit) {\n dispatch(commitMasterDataFailure(error));\n return;\n }\n\n const { status, data } = error.response;\n\n if (status === ResponseCode.BAD_REQUEST) {\n dispatch(uploadMasterDataBadRequest({\n errorCode: data.errorCode,\n args: data.args,\n message: data.message,\n }));\n \n return;\n }\n\n dispatch(uploadMasterDataFailure(error.response))\n }\n }\n);\n","import { StoreModel } from '../store.model';\nimport { MasterDataUploadResponse } from '../../types/master-data-upload-response.types';\n\nexport const isLoadingSelector = (state: StoreModel): boolean => state.masterDataUpload.isLoading;\n\nexport const responseSelector = (state: StoreModel): MasterDataUploadResponse => state.masterDataUpload.response;\n\nexport const isLoadingCommitSelector = (state: StoreModel): boolean => state.masterDataUpload.isLoadingCommit;\n\nexport const committedSuccessfullySelector = (state: StoreModel): boolean => state.masterDataUpload.committedSuccessfully;\n\nexport const errorCommitSelector = (state: StoreModel): any => state.masterDataUpload.errorCommit;\n","export type ChangesOutcome = {\n total: number;\n added: number;\n updated?: number;\n deleted: number;\n};\n\nexport enum ChangesItem {\n MATERIALS = 'materials',\n MATERIAL_LINKS = 'material-links',\n AIR_CAVITY_THICKNESSES = 'air-cavity-thicknesses',\n};\n\nexport type BadRequest = {\n errorCode?: string;\n args?: { [key: string]: string };\n message?: string;\n};\n\nexport type Ok = {\n countryId?: string;\n countryName?: string;\n constructionTypeId?: string;\n constructionTypeNameLocalized?: string;\n changes?: {\n [key in ChangesItem]: ChangesOutcome;\n };\n};\n\nexport type MasterDataUploadResponse = {\n status?: number;\n ok?: Ok;\n badRequest?: BadRequest;\n};\n","import React from 'react';\nimport { Ok, ChangesItem } from '../../types/master-data-upload-response.types';\nimport { useTranslation } from 'react-i18next';\nimport { Box, List, ListItem, ListItemText, ListItemIcon } from '@material-ui/core';\nimport ArrowRight from '@material-ui/icons/ArrowRight';\n\nexport default function CommitMessage(props: Ok) {\n\n const { changes } = props;\n\n const { t } = useTranslation();\n\n const countryConstructionTypeMessage = props.countryId && props.constructionTypeId &&\n `${t('master-data-upload-messages.ok.countryConstructionTypeWillBeUpdated', {\n country: props.countryName,\n constructionType: props.constructionTypeNameLocalized\n })}`;\n\n const renderChanges = (item: ChangesItem) => {\n const change = changes![item];\n return Object.keys(change).filter(key => key !== 'softDeleted' && key !== 'hardDeleted').map((key, index) => {\n // @ts-ignore\n const count = change[key];\n return (\n \n \n \n \n );\n });\n }\n\n return (\n \n Before you commit consider the following: \n \n \n {countryConstructionTypeMessage && \n \n \n }\n
\n \n \n \n {renderChanges(ChangesItem.MATERIALS)}\n
\n \n \n \n {renderChanges(ChangesItem.MATERIAL_LINKS)}\n
\n \n \n \n {renderChanges(ChangesItem.AIR_CAVITY_THICKNESSES)}\n
\n \n \n );\n}\n","import React from 'react';\nimport { BadRequest } from '../../types/master-data-upload-response.types';\nimport { makeStyles, Theme, createStyles, Box } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\n\nconst useStyle = makeStyles((theme: Theme) => (\n createStyles({\n message: {\n color: theme.palette.error.main,\n }\n })\n))\n\nexport default function ValidationMessage(props: BadRequest) {\n\n const { message } = props;\n\n const classes = useStyle();\n\n return (\n \n
Your master data has the following validation error: \n
\n
\n )\n}\n","import React from 'react';\nimport { Box, CircularProgress, Button } from '@material-ui/core';\nimport { isEmpty } from 'lodash';\n\nexport type ChooseFileProps = {\n fileName: string;\n isLoading: boolean;\n isSecondary: boolean;\n handleChange: (event: any) => void;\n committedSuccessfully?: boolean;\n};\n\nexport default function ChooseFile(props: ChooseFileProps) {\n\n const { fileName, isLoading, isSecondary, handleChange, committedSuccessfully } = props;\n\n return (\n \n {isEmpty(fileName) || committedSuccessfully\n ? No file selected \n : File name: {fileName} \n }\n\n {isLoading && }\n\n \n \n {isSecondary\n ? Choose a different file \n : Choose file \n }\n \n \n \n \n );\n};\n","import React, { useState } from 'react';\nimport { Button, Box, Divider, CircularProgress } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { useDispatch, useSelector } from 'react-redux';\nimport { uploadMasterData } from '../../store/master-data-upload/master-data-upload.action-creators';\nimport { hasTechnicalRoleSelector } from '../../store/user/user.selectors';\nimport {\n isLoadingSelector,\n responseSelector,\n isLoadingCommitSelector,\n committedSuccessfullySelector,\n errorCommitSelector\n} from '../../store/master-data-upload/master-data-upload.selectors';\nimport { Trans } from 'react-i18next';\nimport { isEmpty } from 'lodash';\nimport CommitMessage from './CommitMessage';\nimport ValidationMessage from './ValidationMessage';\nimport ChooseFile from './ChooseFile';\n\nexport default function MasterDataUpload() {\n\n const dispatch = useDispatch();\n\n const hasTechnicalRole = useSelector(hasTechnicalRoleSelector);\n const isLoading = useSelector(isLoadingSelector);\n const response = useSelector(responseSelector);\n const isLoadingCommit = useSelector(isLoadingCommitSelector);\n const committedSuccessfully = useSelector(committedSuccessfullySelector);\n const errorCommit = useSelector(errorCommitSelector);\n\n const [fileName, setFileName] = useState('');\n const [fileAsBlob, setFileAsBlob] = useState('');\n\n const readyToUpload = () => fileAsBlob !== '';\n\n const handleChooseFileChange = (event: any) => {\n if (event.target.files && event.target.files[0]) {\n setFileName(event.target.files[0].name);\n setFileAsBlob(event.target.files[0]);\n dispatch(uploadMasterData(event.target.files[0], event.target.files[0].name));\n\n // Clear out the HTML input control's selected file, allowing the user to select a new file, or even reselect the same file.\n // This does not clear out the blob reference we have, and as long as the file doesn't change on disk before commiting, the commit changes still works.\n event.target.value = null;\n }\n };\n\n const handleCommitChangesOnClick = () => {\n if (readyToUpload()) {\n const commit = true;\n dispatch(uploadMasterData(fileAsBlob, fileName, commit));\n }\n };\n\n return (\n \n
Upload master data \n\n {!hasTechnicalRole ?
No Permission
\n :
\n
\n \n \n\n {committedSuccessfully &&
Your master data changes have been applied successfully }\n\n {!isEmpty(response.ok) &&\n
\n \n \n \n \n\n {isLoadingCommit && }\n\n \n Commit changes \n \n\n {!isEmpty(errorCommit) && {errorCommit.message} }\n \n }\n\n {!isEmpty(response.badRequest) &&\n
\n \n \n \n \n \n }\n
}\n\n
\n );\n};\n","import { CalculationsExportRowCount } from '../../types/domain/calculations-export-types';\n\nimport {\n ExportCalculationsAttempt,\n EXPORT_CALCULATIONS_ATTEMPT,\n ExportCalculationsSuccess,\n EXPORT_CALCULATIONS_SUCCESS,\n ExportCalculationsFailure,\n EXPORT_CALCULATIONS_FAILURE,\n SetError,\n SET_ERROR,\n} from './calculations-export.action-types';\n\nexport const exportCalculationsAttempt = (): ExportCalculationsAttempt => ({\n type: EXPORT_CALCULATIONS_ATTEMPT,\n});\n\nexport const exportCalculationsSuccess = (exportRowCount: CalculationsExportRowCount): ExportCalculationsSuccess => ({\n type: EXPORT_CALCULATIONS_SUCCESS,\n payload: exportRowCount,\n});\n\nexport const exportCalculationsFailure = (error: string): ExportCalculationsFailure => ({\n type: EXPORT_CALCULATIONS_FAILURE,\n error: error,\n});\n\nexport const setError = (error: string | null): SetError => ({\n type: SET_ERROR,\n error: error,\n});\n","import { exportCalculationsAttempt, exportCalculationsSuccess, exportCalculationsFailure } from \"./calculations-export.actions\"\nimport { CalculationsExportRowCount } from '../../types/domain/calculations-export-types';\nimport { getAuthProvider } from \"../../authentication/authProvider\";\nimport axios from \"axios\";\n\nexport const exportCalculations = (fromDate: Date, toDate: Date) => (\n async (dispatch: any) => {\n\n dispatch(exportCalculationsAttempt());\n\n try {\n const token = await getAuthProvider().getIdToken();\n const response = await axios.get('api/calculations', {\n headers: { 'Authorization': `Bearer ${token.idToken.rawIdToken}` },\n responseType: 'blob',\n params: {\n from: formatDate(fromDate),\n to: formatDate(toDate),\n },\n });\n\n const exportRowCount: CalculationsExportRowCount = {\n count: `${response.headers['export-row-count']}`,\n limitReached: response.headers['export-row-count-limit-reached'] === 'true'\n };\n\n // Creating link to the Blob\n const url = window.URL.createObjectURL(new Blob([response.data]));\n const link = document.createElement('a');\n link.href = url;\n link.setAttribute('download', `KICS-Customer-Calculations.xlsx`);\n document.body.appendChild(link);\n link.click();\n link.parentNode?.removeChild(link);\n\n dispatch(exportCalculationsSuccess(exportRowCount));\n } catch(error) {\n dispatch(exportCalculationsFailure(error.response.statusText));\n }\n\n }\n)\n\nconst formatDate = (date: Date): string => {\n return `${date.getFullYear()}-${appendLeadingZero(date.getMonth() + 1)}-${appendLeadingZero(date.getDate())}T00:00:00.000Z`;\n}\n\nconst appendLeadingZero = (n: number): string => n <= 9 ? '0' + n : n.toString();\n","import { StoreModel } from '../store.model';\nimport { CalculationsExportRowCount } from '../../types/domain/calculations-export-types';\n\nexport const isLoadingSelector = (state: StoreModel): boolean => state.calculationsExport.isLoading;\n\nexport const errorSelector = (state: StoreModel): string | null => state.calculationsExport.error;\n\nexport const exportRowCountSelector = (state: StoreModel): CalculationsExportRowCount | null => state.calculationsExport.exportRowCount;\n","import 'date-fns';\nimport React, { useState } from 'react';\nimport { MuiPickersUtilsProvider, KeyboardDatePicker } from '@material-ui/pickers';\nimport DateFnsUtils from '@date-io/date-fns';\nimport { compareAsc, compareDesc } from 'date-fns';\nimport { Grid, Box } from '@material-ui/core';\nimport { useTranslation } from 'react-i18next';\nimport { isEmpty } from 'lodash';\nimport { Alert } from '@material-ui/lab';\n\nexport type DatePickersProps = {\n fromDate: Date | null;\n toDate: Date | null;\n setFromDate: (date: Date | null) => void;\n setToDate: (date: Date | null) => void;\n};\n\nexport default function DatePickers(props: DatePickersProps) {\n\n const { fromDate, toDate, setFromDate, setToDate } = props;\n\n const { t } = useTranslation();\n\n const [ error, setError ] = useState(null);\n\n const handleFromDateChange = (date: Date | null) => {\n if (date === null) {\n return;\n }\n \n if (toDate === null) {\n setFromDate(date);\n return;\n }\n\n if (compareAsc(toDate, date) < 0) {\n setError(t('calculations-export-ui.date-picking-error'));\n return;\n }\n\n setFromDate(date);\n setError(null);\n };\n\n const handleToDateChange = (date: Date | null) => {\n if (date === null) {\n return;\n }\n\n if (fromDate === null) {\n setToDate(date);\n return;\n }\n \n if (compareDesc(fromDate, date) < 0) {\n setError(t('calculations-export-ui.date-picking-error'));\n return;\n }\n\n setToDate(date);\n setError(null);\n };\n\n return (\n \n \n \n \n \n \n \n \n \n \n \n\n {!isEmpty(error) && {error} }\n \n );\n}","import React, { useState, useEffect } from 'react';\nimport { Box, Button, CircularProgress } from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { useDispatch, useSelector } from 'react-redux';\nimport { hasTechnicalRoleSelector, hasMarketingRoleSelector } from '../../store/user/user.selectors';\nimport { exportCalculations } from '../../store/calculations-export/calculations-export.action-creators';\nimport { errorSelector, isLoadingSelector, exportRowCountSelector } from '../../store/calculations-export/calculations-export.selectors';\nimport { isEmpty } from 'lodash';\nimport { Trans, useTranslation } from 'react-i18next';\nimport DatePickers from './DatePickers';\nimport { setError } from '../../store/calculations-export/calculations-export.actions';\n\nexport default function CalculationsExport() {\n\n const dispatch = useDispatch();\n const { t } = useTranslation();\n\n const error = useSelector(errorSelector);\n const isLoading = useSelector(isLoadingSelector);\n const exportRowCount = useSelector(exportRowCountSelector);\n\n const hasTechnicalRole = useSelector(hasTechnicalRoleSelector);\n const hasMarketingRole = useSelector(hasMarketingRoleSelector);\n\n const hasAccess = hasTechnicalRole || hasMarketingRole;\n\n const [fromDate, setFromDate] = useState(null);\n const [toDate, setToDate] = useState(null);\n\n const handleExportOnClick = () => {\n if (fromDate === null || toDate === null) {\n dispatch(setError(t('calculations-export-ui.export-validation-error')));\n return;\n }\n\n dispatch(exportCalculations(fromDate, toDate));\n }\n\n useEffect(() => {\n if (fromDate !== null && toDate !== null) {\n dispatch(setError(null));\n }\n }, [dispatch, fromDate, toDate]);\n\n return (\n \n Export Calculations \n\n {!hasAccess ? no-permission
\n : \n
\n \n \n\n {isLoading &&
}\n\n
\n \n Export \n \n \n\n {!isEmpty(error) &&
{error} }\n\n {exportRowCount && (\n
\n
{t('calculations-export-ui.success', { count: exportRowCount.count as any })} \n\n {exportRowCount.limitReached &&
{t('calculations-export-ui.warning', { count: exportRowCount.count as any })} }\n
\n )}\n
}\n \n );\n};\n","import {StoreModel} from \"../store.model\";\nimport {ConstructionTypeNewsItemsModel, NewsItemTableScreenModel} from \"../../types/domain/news-items.types\";\n\nexport const countryIdSelector = (state: StoreModel): string => state.newsItems.countryId;\n\nexport const allowedCultureIdsSelector = (state: StoreModel): string[] => state.newsItems.allowedCultures;\n\nexport const newsItemTableStateSelector = (state: StoreModel): NewsItemTableScreenModel => state.newsItems.newsItemTableScreen;\n\nexport const constructionTypeNewsItemsSelector = (state: StoreModel): ConstructionTypeNewsItemsModel => state.newsItems.constructionTypeNewsItems;\n","import axios from 'axios';\nimport {getAuthProvider} from '../../authentication/authProvider';\nimport {StoreModel} from '../store.model'\nimport {\n getConstructionTypeNewsItemsAttempt,\n getConstructionTypeNewsItemsFailure,\n getConstructionTypeNewsItemsSuccess,\n getNewsItemsAttempt,\n getNewsItemsFailure,\n getNewsItemsSuccess\n} from \"./news-items.actions\";\nimport {ConstructionTypeNewsItemUpdateModel, NewsItemData} from \"../../types/domain/news-items.types\";\nimport {isEmpty} from \"lodash\";\n\n\nexport type RequestResult = {\n success: boolean,\n error: string\n}\n\nconst urlRegex = new RegExp('https?://');\n\nconst urlHasProtocol = (url: string) => {\n return urlRegex.test(url);\n}\n\nconst urlIsBlank = (url: string) => {\n return url.replace(urlRegex, '') === '';\n}\n\nexport const createOrUpdateNewsItem = (data: NewsItemData) => async (dispatch: any): Promise => {\n\n let result: RequestResult = {success: false, error: \"\"};\n\n if (isEmpty(data.title)) {\n result.error = 'news-items-ui.validation-errors.missing-title';\n return result;\n }\n \n for (const item of data.newsItemContents) {\n if (isEmpty(item.url) || isEmpty(item.subject)) {\n result.error = 'news-items-ui.validation-errors.empty-fields';\n return result;\n }\n if (!urlHasProtocol(item.url)) {\n result.error = 'news-items-ui.validation-errors.no-protocol';\n return result;\n }\n if (urlIsBlank(item.url)) {\n result.error = 'news-items-ui.validation-errors.empty-url';\n return result;\n }\n }\n\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.post(`api/NewsItems`, data,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n\n if (requestResult.status === 200) {\n dispatch(loadNewsItems());\n\n result.success = true;\n return result;\n }\n } catch (error) {\n result.error = error.message;\n }\n\n return result;\n}\n\nexport const loadNewsItems = () => async (dispatch: any, getState: () => StoreModel) => {\n const countryId = getState().newsItems.countryId;\n dispatch(getNewsItemsAttempt());\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.get(`/api/NewsItems?countryId=${countryId}`,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n\n if (requestResult.status === 200) {\n dispatch(getNewsItemsSuccess(requestResult.data));\n }\n } catch (error) {\n dispatch(getNewsItemsFailure(error.message));\n }\n}\n\nexport const deleteNewsItem = (newsItemId: string) => async (dispatch: any): Promise => {\n let result: RequestResult = {success: false, error: \"\"};\n\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.delete(`api/NewsItems/${newsItemId}`,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n\n if (requestResult.status === 200) {\n dispatch(loadNewsItems());\n\n result.success = true;\n return result;\n }\n } catch (error) {\n result.error = error.message;\n }\n\n return result;\n}\n\nexport const loadConstructionTypeNewsItemsList = (countryId: string) => async (dispatch: any) => {\n dispatch(getConstructionTypeNewsItemsAttempt());\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.get(`/api/ConstructionTypesNewsItem?countryId=${countryId}`,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n\n if (requestResult.status === 200) {\n dispatch(getConstructionTypeNewsItemsSuccess(requestResult.data));\n }\n } catch (error) {\n dispatch(getConstructionTypeNewsItemsFailure(error.message));\n }\n}\n\nexport const createOrUpdateConstructionTypeNewsItem = (data: ConstructionTypeNewsItemUpdateModel) => async (dispatch: any): Promise => {\n let result: RequestResult = {success: false, error: \"\"};\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.post(`/api/ConstructionTypesNewsItem`, data,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n\n if (requestResult.status === 200) {\n dispatch(loadConstructionTypeNewsItemsList(data.countryId));\n\n result.success = true;\n return result;\n }\n } catch (error) {\n result.error = error.message;\n }\n\n return result;\n}\n\nexport const deleteConstructionTypeNewsItem = (countryId: string, constructionTypeId: string) => \n async (dispatch: any): Promise => {\n\n let result: RequestResult = {success: false, error: \"\"};\n try {\n const token = await getAuthProvider().getIdToken();\n const requestResult = await axios.delete(`api/ConstructionTypesNewsItem/${countryId}/${constructionTypeId}`,\n {\n headers: {\n \"Authorization\": `Bearer ${token.idToken.rawIdToken}`,\n \"Content-Type\": \"application/json\"\n },\n });\n if (requestResult.status === 200) {\n dispatch(loadConstructionTypeNewsItemsList(countryId));\n \n result.success = true;\n return result;\n }\n } catch (error) {\n result.error = error.message;\n }\n\n return result;\n }\n","import {\n GET_CONSTRUCTION_TYPE_NEWS_ITEMS_ATTEMPT,\n GET_CONSTRUCTION_TYPE_NEWS_ITEMS_FAILURE,\n GET_CONSTRUCTION_TYPE_NEWS_ITEMS_SUCCESS,\n GET_NEWS_ITEM_ATTEMPT,\n GET_NEWS_ITEM_FAILURE,\n GET_NEWS_ITEM_SUCCESS,\n GetConstructionTypeNewsItemsAttempt,\n GetConstructionTypeNewsItemsFailure,\n GetConstructionTypeNewsItemsSuccess,\n GetNewsItemsAttempt,\n GetNewsItemsFailure,\n GetNewsItemsSuccess,\n SET_ALLOWED_CULTURE_IDS,\n SET_COUNTRY_ID,\n SET_ERROR,\n SetAllowedCulturesId,\n SetCountryId,\n SetError,\n} from \"./news-items.action-types\";\nimport {ConstructionTypeNewsItemData, NewsItemData} from \"../../types/domain/news-items.types\";\n\n\nexport const setCountryId = (countryId: string): SetCountryId => ({\n type: SET_COUNTRY_ID,\n payload: countryId,\n});\n\nexport const setAllowedCulturesId = (allowedCultures: string[]): SetAllowedCulturesId => ({\n type: SET_ALLOWED_CULTURE_IDS,\n payload: allowedCultures,\n});\n\nexport const setError = (error: any): SetError => ({\n type: SET_ERROR,\n error: error,\n});\n\nexport const getNewsItemsAttempt = (): GetNewsItemsAttempt => ({\n type: GET_NEWS_ITEM_ATTEMPT\n});\n\nexport const getNewsItemsSuccess = (newsItemsData: NewsItemData[]): GetNewsItemsSuccess => ({\n type: GET_NEWS_ITEM_SUCCESS,\n payload: newsItemsData\n});\n\nexport const getNewsItemsFailure = (error: any): GetNewsItemsFailure => ({\n type: GET_NEWS_ITEM_FAILURE,\n error: error\n});\n\nexport const getConstructionTypeNewsItemsAttempt = (): GetConstructionTypeNewsItemsAttempt => ({\n type: GET_CONSTRUCTION_TYPE_NEWS_ITEMS_ATTEMPT\n});\n\nexport const getConstructionTypeNewsItemsSuccess = (constructionTypeNewsItemData: ConstructionTypeNewsItemData[]):\n GetConstructionTypeNewsItemsSuccess => ({\n type: GET_CONSTRUCTION_TYPE_NEWS_ITEMS_SUCCESS,\n payload: constructionTypeNewsItemData\n});\n\nexport const getConstructionTypeNewsItemsFailure = (error: any): GetConstructionTypeNewsItemsFailure => ({\n type: GET_CONSTRUCTION_TYPE_NEWS_ITEMS_FAILURE,\n error: error\n});\n","import React from \"react\";\nimport Button from \"@material-ui/core/Button\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Dialog from \"@material-ui/core/Dialog\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport { Trans, useTranslation } from \"react-i18next\";\nimport { uuid } from \"../../../common/uuid\";\nimport { Box, Grid } from \"@material-ui/core\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { allowedCultureIdsSelector, countryIdSelector, } from \"../../../store/news-items/news-items.selectors\";\nimport { createOrUpdateNewsItem } from \"../../../store/news-items/news-items.action-creators\";\nimport { isEmpty } from \"lodash\";\nimport { Alert } from \"@material-ui/lab\";\nimport { ThunkDispatch } from \"redux-thunk\";\nimport { AnyAction } from \"redux\";\nimport { NewsItemContent, NewsItemData } from \"../../../types/domain/news-items.types\";\n\ntype AppDispatch = ThunkDispatch;\n\nconst createNewsItemModel = (allowedCultureIds: string[]): { [newsItemContentId: string]: NewsItemContent } => {\n return allowedCultureIds.map(cultureId => ({\n newsItemContentId: uuid(),\n culture: cultureId,\n subject: \"\",\n url: \"\"\n } as NewsItemContent)).reduce((obj, item) => {\n return {\n ...obj,\n [item.newsItemContentId]: item\n };\n }, {});\n}\n\nexport type CreateNewsItemDialogProps = {\n onClose: () => void,\n};\n\nexport const CreateNewsItemDialog = (props: CreateNewsItemDialogProps) => {\n const { t } = useTranslation();\n const dispatch: AppDispatch = useDispatch();\n\n const selectedCountryId = useSelector(countryIdSelector);\n const allowedCultureIds = useSelector(allowedCultureIdsSelector);\n\n const [title, setTitle] = React.useState(\"\");\n const [newsItemContents, setNewsItemContents] = React.useState(createNewsItemModel(allowedCultureIds));\n const [requestResult, setRequestResult] = React.useState({ isLoading: false, error: \"\" });\n\n const { isLoading, error } = requestResult;\n\n const onUpdateItem = (id: string, updatedField: object) => {\n const newsItem = newsItemContents[id];\n\n setNewsItemContents((prevState) => {\n return {\n ...prevState,\n [newsItem.newsItemContentId]: {\n ...newsItem,\n ...updatedField\n }\n };\n });\n };\n\n const onSubmit = async () => {\n if (!requestResult.isLoading) {\n setRequestResult(prevState => ({ ...prevState, isLoading: true }))\n\n const data = {\n title: title,\n countryId: selectedCountryId,\n newsItemContents: Object.values(newsItemContents).map(item => (\n {\n culture: item.culture,\n subject: item.subject,\n url: item.url\n } as NewsItemContent\n ))\n } as NewsItemData;\n\n let result = await dispatch(createOrUpdateNewsItem(data));\n\n if (result.success) {\n props.onClose()\n } else {\n setRequestResult(prevState => ({ ...prevState, isLoading: false, error: t(result.error) }))\n }\n }\n }\n\n const newsItems = Object.values(newsItemContents).map((item) => {\n return (\n \n {t(`culture-names.${item.culture}`)} \n \n onUpdateItem(item.newsItemContentId, { subject: e.target.value as string })}\n fullWidth\n />\n \n \n onUpdateItem(item.newsItemContentId, { url: e.target.value as string })}\n fullWidth\n />\n \n \n );\n })\n\n return (\n \n \n \n Create News item\n \n \n \n setTitle(e.target.value as string)}\n fullWidth\n />\n {newsItems}\n {!isEmpty(error) && {error} }\n \n \n \n Cancel \n \n \n Create \n \n \n \n );\n};\n","import { v4 } from 'uuid';\n\n// For some reason its really hard to mock the uuid module directly,\n// so using this wrapper will all us to mock it in tests with low impact on code\nexport function uuid(): string {\n return v4();\n}\n","import Box from \"@material-ui/core/Box\";\nimport React from \"react\";\n\nimport { countryIdsSelector } from '../../../store/user/user.selectors';\n\nimport CountrySelection from '../../CountrySelection/CountrySelection';\nimport { fetchConstructionTypes } from '../../../store/construction-data/construction-data.action-creators';\nimport {useDispatch, useSelector} from \"react-redux\";\nimport {countryIdSelector} from \"../../../store/news-items/news-items.selectors\";\nimport {setAllowedCulturesId, setCountryId} from \"../../../store/news-items/news-items.actions\";\nimport { getAppConfig } from \"../../../config/config\";\n\nexport function SelectCountry() {\n const dispatch = useDispatch();\n const { supportedCulturesPerCountry } = getAppConfig();\n\n const countryIds = useSelector(countryIdsSelector);\n const selectedCountryId = useSelector(countryIdSelector);\n\n const handleCountryChange = React.useCallback((countryId: string) => {\n const allowedCultures = supportedCulturesPerCountry[countryId];\n\n dispatch(setCountryId(countryId));\n dispatch(fetchConstructionTypes(countryId));\n dispatch(setAllowedCulturesId(allowedCultures));\n }, [dispatch, supportedCulturesPerCountry]);\n\n React.useEffect(() => {\n if (!selectedCountryId) {\n handleCountryChange(countryIds[0]);\n }\n }, [countryIds, selectedCountryId, handleCountryChange])\n\n return \n handleCountryChange(e.target.value as string)}\n />\n \n}\n","import React, {forwardRef} from 'react';\nimport MaterialTable, {Icons} from 'material-table';\nimport AddBox from '@material-ui/icons/AddBox';\nimport ChevronRight from '@material-ui/icons/ChevronRight';\nimport Clear from '@material-ui/icons/Clear';\nimport Edit from '@material-ui/icons/Edit';\nimport Search from '@material-ui/icons/Search';\nimport {Box, CircularProgress, Grid} from \"@material-ui/core\";\nimport {useDispatch, useSelector} from \"react-redux\";\nimport {\n countryIdSelector,\n newsItemTableStateSelector\n} from \"../../store/news-items/news-items.selectors\";\nimport {loadNewsItems} from \"../../store/news-items/news-items.action-creators\";\nimport {isEmpty} from \"lodash\";\nimport {Alert} from \"@material-ui/lab\";\nimport {ArrowUpward} from \"@material-ui/icons\";\nimport {Trans, useTranslation} from 'react-i18next';\n\n\nconst tableIcons: Icons = {\n Add: forwardRef((props, ref) => ),\n Edit: forwardRef((props, ref) => ),\n Clear: forwardRef((props, ref) => ),\n DetailPanel: forwardRef((props, ref) => ),\n ResetSearch: forwardRef((props, ref) => ),\n Search: forwardRef((props, ref) => ),\n SortArrow: forwardRef((props, ref: React.Ref) => ),\n};\n\ntype ManageNewsTableProps = {\n setShowCreateItemDialog: React.Dispatch>,\n setEditCreateItemDialog: React.Dispatch>,\n}\n\nexport interface INewsItems {\n newsItemId: string;\n countryId: string;\n newsItemContents: (INewsItemContentsEntity)[];\n}\n\nexport interface INewsItemContentsEntity {\n newsItemContentId: string;\n culture: string;\n subject: string;\n url: string;\n}\n\nexport default function ManageNewsTable(props: ManageNewsTableProps) {\n const dispatch = useDispatch();\n const {t} = useTranslation();\n\n const selectedCountryId = useSelector(countryIdSelector);\n\n const tableState = useSelector(newsItemTableStateSelector);\n const {isLoading, error, newsItemsData} = tableState;\n\n React.useEffect(() => {\n if (selectedCountryId) {\n dispatch(loadNewsItems());\n }\n }, [dispatch, selectedCountryId])\n\n const data = newsItemsData.map(news => news.newsItemContents.map(newsItem => {\n const culture = t(`culture-names.${newsItem.culture}`);\n\n return {\n title: news.title,\n subject: newsItem.subject,\n url: newsItem.url,\n culture: culture\n };\n })).flat();\n\n return (\n \n {isLoading && }\n {!isLoading && \n Manage News Items\n }\n columns={[\n {title: 'Title', field: 'title', defaultGroupOrder: 0},\n {title: 'Subject', field: 'subject'},\n {title: 'URL', field: 'url'},\n {title: 'Language', field: 'culture'},\n ]}\n data={data}\n options={{\n grouping: false,\n paging: false\n }}\n actions={[\n {\n icon: () => ,\n tooltip: 'Create News Item',\n isFreeAction: true,\n onClick: () => props.setShowCreateItemDialog(true)\n },\n {\n icon: () => ,\n tooltip: 'Edit News Item',\n isFreeAction: true,\n disabled: data.length === 0,\n onClick: () => props.setEditCreateItemDialog(true)\n }\n ]}\n />\n }\n {!isLoading && !isEmpty(error) && {error} }\n \n );\n}\n","import React from \"react\";\nimport Dialog from \"@material-ui/core/Dialog\";\nimport DialogContent from \"@material-ui/core/DialogContent\";\nimport DialogTitle from \"@material-ui/core/DialogTitle\";\nimport { Trans, useTranslation } from \"react-i18next\";\nimport { Box, Grid, InputLabel, MenuItem, Select } from \"@material-ui/core\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { ThunkDispatch } from \"redux-thunk\";\nimport { AnyAction } from \"redux\";\nimport { newsItemTableStateSelector } from \"../../../store/news-items/news-items.selectors\";\nimport TextField from \"@material-ui/core/TextField\";\nimport Button from \"@material-ui/core/Button\";\nimport DialogActions from \"@material-ui/core/DialogActions\";\nimport { createOrUpdateNewsItem, deleteNewsItem } from \"../../../store/news-items/news-items.action-creators\";\nimport { createStyles, makeStyles, Theme } from \"@material-ui/core/styles\";\nimport FormControl from \"@material-ui/core/FormControl\";\nimport { isEmpty } from \"lodash\";\nimport { Alert } from \"@material-ui/lab\";\nimport DialogContentText from \"@material-ui/core/DialogContentText\";\nimport { NewsItemData } from \"../../../types/domain/news-items.types\";\n\ntype AppDispatch = ThunkDispatch;\n\nexport type EditNewsItemDialogProps = {\n onClose: () => void,\n};\n\nconst useStyles = makeStyles((theme: Theme) =>\n createStyles({\n cancel: {\n flex: 1,\n alignSelf: \"flex-end\",\n justifyContent: \"left\"\n },\n formControl: {\n marginBottom: theme.spacing(5),\n },\n primaryAction: {\n minWidth: \"120px\"\n }\n })\n);\n\nexport const EditNewsItemDialog = (props: EditNewsItemDialogProps) => {\n const { t } = useTranslation();\n const classes = useStyles();\n\n const dispatch: AppDispatch = useDispatch();\n const tableState = useSelector(newsItemTableStateSelector);\n\n const { newsItemsData } = tableState;\n\n const [selectedNewsItem, setSelectedNewsItem] = React.useState();\n const [requestResult, setRequestResult] = React.useState({ isLoading: false, error: \"\" });\n const [showDeleteView, setShowDeleteView] = React.useState(false);\n\n const { isLoading, error } = requestResult;\n\n const onSelectNewsItem = (newsItemId: string) => {\n const newsItems = newsItemsData.find(x => x.newsItemId === newsItemId);\n setSelectedNewsItem(newsItems);\n }\n\n const onUpdateNewsTitle = (newTitle: string) => {\n setSelectedNewsItem(prevState => {\n if (prevState) {\n return { ...prevState, title: newTitle }\n }\n return prevState;\n });\n }\n\n const onUpdateNewsField = (newsItemContentId: string, updatedField: object) => {\n setSelectedNewsItem(prevState => {\n if (prevState) {\n let newsItemContentIdx = prevState.newsItemContents.findIndex(item => item.newsItemContentId === newsItemContentId);\n const newsItemContent = prevState.newsItemContents[newsItemContentIdx];\n return {\n ...prevState,\n newsItemContents: [\n ...prevState.newsItemContents.slice(0, newsItemContentIdx),\n { ...newsItemContent, ...updatedField },\n ...prevState.newsItemContents.slice(newsItemContentIdx + 1)\n ]\n }\n }\n return prevState;\n });\n }\n\n const onSubmit = async () => {\n if (!requestResult.isLoading) {\n setRequestResult(prevState => ({ ...prevState, isLoading: true }))\n if (selectedNewsItem) {\n let result = await dispatch(createOrUpdateNewsItem(selectedNewsItem));\n if (result.success) {\n props.onClose()\n } else {\n setRequestResult(prevState => ({ ...prevState, isLoading: false, error: t(result.error), success: result.success }))\n }\n }\n }\n }\n\n const onDeleteItem = async () => {\n if (!requestResult.isLoading && selectedNewsItem) {\n setRequestResult(prevState => ({ ...prevState, isLoading: true }))\n\n let result = await dispatch(deleteNewsItem(selectedNewsItem.newsItemId));\n\n if (result.success) {\n props.onClose()\n } else {\n setRequestResult(prevState => ({ ...prevState, isLoading: false, error: result.error }))\n }\n }\n }\n\n return (\n \n \n \n Edit News Item\n \n \n {!showDeleteView &&\n \n {!selectedNewsItem &&\n \n \n \n \n Select News Title\n \n \n onSelectNewsItem(e.target.value as string)}\n >\n {newsItemsData.map(newsItem => (\n \n {newsItem.title}\n \n ))}\n \n \n \n \n Cancel \n \n\n \n
}\n {selectedNewsItem && \n <>\n onUpdateNewsTitle(e.target.value as string)}\n fullWidth\n />\n {selectedNewsItem.newsItemContents.map(item => {\n return (\n \n {t(`culture-names.${item.culture}`)} \n \n onUpdateNewsField(item.newsItemContentId, { subject: e.target.value as string })}\n fullWidth\n />\n onUpdateNewsField(item.newsItemContentId, { url: e.target.value as string })}\n fullWidth\n />\n \n \n )\n })}\n \n \n Cancel \n \n setShowDeleteView(true)}\n >\n \n Delete\n \n \n \n Save \n \n \n >\n
}\n {!isLoading && !isEmpty(error) && {error} }\n \n }\n {showDeleteView && selectedNewsItem &&\n \n \n \n \n You are about to delete the following Item: {selectedNewsItem.title} \n \n \n \n setShowDeleteView(false)} color=\"primary\">\n Cancel\n \n \n Ok\n \n \n
}\n \n );\n};\n","import React, { forwardRef } from 'react';\nimport MaterialTable, { Icons } from 'material-table';\nimport Clear from '@material-ui/icons/Clear';\nimport Edit from '@material-ui/icons/Edit';\nimport Search from '@material-ui/icons/Search';\nimport { CircularProgress, Box, Grid } from \"@material-ui/core\";\nimport { ArrowUpward, Check } from \"@material-ui/icons\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { createOrUpdateConstructionTypeNewsItem, deleteConstructionTypeNewsItem, } from \"../../../store/news-items/news-items.action-creators\";\nimport { constructionTypeNewsItemsSelector, countryIdSelector } from \"../../../store/news-items/news-items.selectors\";\nimport { ConstructionTypeNewsItemUpdateModel } from \"../../../types/domain/news-items.types\";\nimport { useManageAssociationState, Row, unassignedNewsItemId } from \"./use-manage-association.state\";\nimport { Trans } from \"react-i18next\";\n\nconst tableIcons: Icons = {\n Edit: forwardRef((props, ref) => ),\n Clear: forwardRef((props, ref) => ),\n ResetSearch: forwardRef((props, ref) => ),\n Search: forwardRef((props, ref) => ),\n SortArrow: forwardRef((props, ref: React.Ref) => ),\n Check: forwardRef((props, ref) => ),\n};\n\nexport default function ManageAssociationTable() {\n const dispatch = useDispatch();\n const constructionTypeNewsItems = useSelector(constructionTypeNewsItemsSelector);\n const selectedCountryId = useSelector(countryIdSelector);\n\n const { tableData, tableColumns } = useManageAssociationState();\n\n const onUpdate = (newData: Row): Promise => {\n return new Promise((resolve) => {\n const constructionTypeNewsItem = constructionTypeNewsItems.constructionTypeNewsItemsData.find(item => item.constructionTypeId === newData.constructionTypeId);\n\n if (newData.newsItemId === unassignedNewsItemId && constructionTypeNewsItem) {\n dispatch(deleteConstructionTypeNewsItem(constructionTypeNewsItem.countryId, constructionTypeNewsItem.constructionTypeId));\n } else if (constructionTypeNewsItem) {\n // item already exists -> update\n const payload: ConstructionTypeNewsItemUpdateModel = {\n constructionTypeId: constructionTypeNewsItem.constructionTypeId,\n countryId: constructionTypeNewsItem.countryId,\n newsItemId: newData.newsItemId\n };\n dispatch(createOrUpdateConstructionTypeNewsItem(payload));\n } else if (newData.newsItemId !== unassignedNewsItemId){\n // create new item\n const payload: ConstructionTypeNewsItemUpdateModel = {\n constructionTypeId: newData.constructionTypeId,\n countryId: selectedCountryId,\n newsItemId: newData.newsItemId\n };\n dispatch(createOrUpdateConstructionTypeNewsItem(payload));\n }\n resolve();\n });\n }\n\n return (\n \n {constructionTypeNewsItems.isLoading && }\n {!constructionTypeNewsItems.isLoading && \n \n Associate News Items\n }\n icons={tableIcons}\n columns={tableColumns}\n data={tableData}\n options={{\n paging: false,\n actionsColumnIndex: -1\n }}\n editable={{\n onRowUpdate: (newData, _) => onUpdate(newData)\n }}\n />\n \n }\n \n );\n}\n","import React from \"react\";\n\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { useTranslation } from \"react-i18next\";\nimport { constructionTypesSelector } from \"../../../store/construction-data/construction-data.selectors\";\nimport {\n constructionTypeNewsItemsSelector,\n countryIdSelector,\n newsItemTableStateSelector\n} from \"../../../store/news-items/news-items.selectors\";\nimport { loadConstructionTypeNewsItemsList } from \"../../../store/news-items/news-items.action-creators\";\n\nimport { Column } from \"material-table\";\n\nexport interface Row {\n constructionTypeId: string;\n constructionTypeNameLocalized: string;\n newsItemId: string;\n}\n\nexport interface TableState {\n tableColumns: Array>;\n tableData: Row[];\n}\n\nexport const unassignedNewsItemId = 'unassigned-news-item' as string;\n\nexport function useManageAssociationState() {\n const dispatch = useDispatch();\n const { t } = useTranslation();\n const allConstructionTypes = useSelector(constructionTypesSelector);\n const selectedCountryId = useSelector(countryIdSelector);\n const constructionTypeNewsItems = useSelector(constructionTypeNewsItemsSelector);\n const newsItemTableState = useSelector(newsItemTableStateSelector);\n const { newsItemsData } = newsItemTableState;\n\n React.useEffect(() => {\n if (selectedCountryId) {\n if (selectedCountryId) {\n dispatch(loadConstructionTypeNewsItemsList(selectedCountryId));\n }\n }\n }, [dispatch, selectedCountryId]);\n\n const getAssignedConstructionNewsItems = React.useCallback((): Row[] => {\n return constructionTypeNewsItems.constructionTypeNewsItemsData.map(item => ({\n constructionTypeId: item.constructionTypeId,\n constructionTypeNameLocalized: allConstructionTypes.find(ct => ct.id === item.constructionTypeId)?.constructionTypeNameLocalized ?? item.constructionTypeId,\n newsItemTitle: item.newsItemTitle,\n newsItemId: item.newsItemId\n }));\n }, [constructionTypeNewsItems, allConstructionTypes]);\n\n\n const getUnassignedConstructionNewsItems = React.useCallback((): Row[] => {\n const assignedConstructionTypeIds = constructionTypeNewsItems.constructionTypeNewsItemsData.map(x => x.constructionTypeId)\n return allConstructionTypes.filter(c => !assignedConstructionTypeIds.includes(c.id)).map(x => ({\n constructionTypeId: x.id,\n constructionTypeNameLocalized: x.constructionTypeNameLocalized,\n newsItemTitle: '',\n newsItemId: unassignedNewsItemId\n }) as Row);\n }, [constructionTypeNewsItems.constructionTypeNewsItemsData, allConstructionTypes]);\n\n const getNewsItemLookup = React.useCallback(() => {\n let data: any = { };\n data[unassignedNewsItemId] = t('news-items-ui.unassigned');\n // @ts-ignore\n newsItemsData.forEach(newsItem => data[newsItem.newsItemId] = newsItem.title)\n return data;\n }, [newsItemsData, t])\n\n const createTableState = React.useCallback((data: Row[], lookupItems: {}): TableState => ({\n tableColumns: [\n {\n title: 'Construction Type',\n field: 'constructionTypeNameLocalized',\n editable: 'never'\n },\n {\n title: 'News Item',\n field: 'newsItemId',\n lookup: lookupItems,\n },\n ],\n tableData: data,\n }), [])\n\n const [tableState, setTableState] = React.useState(createTableState([], {}));\n\n React.useEffect(() => {\n const newTableState = createTableState([...getAssignedConstructionNewsItems(), ...getUnassignedConstructionNewsItems()], getNewsItemLookup());\n setTableState(newTableState);\n }, [getNewsItemLookup, getAssignedConstructionNewsItems, getUnassignedConstructionNewsItems, createTableState]);\n\n return tableState;\n}\n","import React from 'react';\nimport Grid from '@material-ui/core/Grid'\nimport { CreateNewsItemDialog } from \"./CreateNewsItemDialog/CreateNewsItemDialog\";\nimport { SelectCountry } from \"./SelectCountry/SelectCountry\";\nimport ManageNewsTable from \"./NewsManagerTable\";\nimport { EditNewsItemDialog } from \"./EditNewsItemDialog/EditNewsItemDialog\";\nimport { useSelector } from \"react-redux\";\nimport { hasMarketingRoleSelector } from \"../../store/user/user.selectors\";\nimport { Trans } from \"react-i18next\";\nimport ManageAssociationTable from \"./ManageAssociationTable/ManageAssociationTable\";\n\nexport default function NewsItems() {\n const [showCreateItemDialog, setShowCreateItemDialog] = React.useState(false);\n const [showEditItemDialog, setShowEditItemDialog] = React.useState(false);\n const hasMarketingRole = useSelector(hasMarketingRoleSelector);\n\n if (!hasMarketingRole) return no-permission
;\n\n return (\n \n \n \n {showCreateItemDialog && setShowCreateItemDialog(false)} />}\n {showEditItemDialog && setShowEditItemDialog(false)} />}\n \n \n \n
\n );\n}\n","import { Box, CircularProgress, Grid } from '@material-ui/core';\nimport Button from '@material-ui/core/Button';\nimport Dialog from '@material-ui/core/Dialog';\nimport DialogActions from '@material-ui/core/DialogActions';\nimport DialogContent from '@material-ui/core/DialogContent';\nimport DialogTitle from '@material-ui/core/DialogTitle';\nimport FormControl from '@material-ui/core/FormControl';\nimport FormControlLabel from '@material-ui/core/FormControlLabel';\nimport FormLabel from '@material-ui/core/FormLabel';\nimport Radio from '@material-ui/core/Radio';\nimport RadioGroup from '@material-ui/core/RadioGroup';\nimport Typography from '@material-ui/core/Typography';\nimport { Alert } from '@material-ui/lab';\nimport axios from 'axios';\nimport React from 'react';\nimport { Trans } from 'react-i18next';\nimport { toast } from 'react-toastify';\n\nimport { getAuthProvider } from '../../authentication/authProvider';\nimport { CountryModel } from './CountryModel';\n\nexport type EditCountryDialogProps = {\n country: CountryModel | null;\n setCountryUnderEdit: React.Dispatch>;\n setCountryUpdated: React.Dispatch>;\n onClose: () => void;\n};\n\nexport const EditCountryDialog = (props: EditCountryDialogProps) => {\n\n const [isLoading, setIsLoading] = React.useState(false);\n\n const handleResultTypeChange = (event: { target: { value: React.SetStateAction; }; }) => {\n props.setCountryUnderEdit((previousCountry) => {\n return {\n ...(previousCountry as CountryModel), resultType: event.target.value as string\n }\n });\n };\n\n const handleIsVisibleToPublicChange = (event: { target: { value: React.SetStateAction; }; }) => {\n const isVisibleToPublic: boolean = event.target.value === \"true\";\n props.setCountryUnderEdit((previousCountry) => {\n return {\n ...(previousCountry as CountryModel), isVisibleToPublic: isVisibleToPublic\n }\n });\n };\n\n const onSave = async () => {\n setIsLoading(true);\n const token = await getAuthProvider().getIdToken();\n\n axios.put(\"/api/countries\", props.country, {\n headers: { \"Authorization\": `Bearer ${token.idToken.rawIdToken}` }\n }).then(function (response) {\n toast.success(props.country?.name + \" updated successfully.\")\n setIsLoading(false);\n props.setCountryUpdated(props.country);\n props.onClose();\n console.info(response);\n }).catch(function (error) {\n toast.error(error);\n console.error(error);\n setIsLoading(false);\n });\n }\n\n return (\n \n \n \n Edit Country\n \n \n\n {\n isLoading && \n }\n\n {\n !isLoading && <>\n \n \n \n \n Country Details\n \n \n \n\n \n \n Country Name\n \n \n \n {props.country?.name}\n \n\n \n \n Country Code\n \n \n \n {props.country?.countryId}\n \n\n \n \n \n \n Result Type\n \n \n \n } label=\"U-value\" />\n } label=\"Rc-value\" />\n \n \n \n\n \n \n \n \n Country visible to the public?\n \n \n \n } label=\"Enabled\" />\n } label=\"Disabled\" />\n \n \n\n \n \n When a country is enabled, that country is visible to the public from the customer facing website with a URL\n containing the country code shown on this page. Changes are applied when you select the save button below.\n \n \n \n\n \n \n \n\n \n \n Cancel \n \n \n Save \n \n \n >\n }\n \n );\n};","import { Box, CircularProgress, Grid } from '@material-ui/core';\nimport Edit from '@material-ui/icons/Edit';\nimport MaterialTable from 'material-table';\nimport React, { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { toast } from 'react-toastify';\n\nimport { getCountries } from '../../common/countries';\nimport { CountryModel } from './CountryModel';\nimport { EditCountryDialog } from './EditCountryDialog';\n\nexport default function ManageCountriesTable() {\n\n const { t } = useTranslation();\n const apiErrorMessage = t(\"countries-ui.countries-table.countries-api-error\");\n\n const [isLoading, setIsLoading] = useState(true);\n const [countryRows, setCountryRows] = useState([]);\n const [showEditCountryDialog, setShowEditCountryDialog] = useState(false);\n const [countryUnderEdit, setCountryUnderEdit] = useState(null);\n const [countryUpdated, setCountryUpdated] = useState(null);\n\n useEffect(() => {\n setIsLoading(true);\n getCountries().then(result => {\n setCountryRows(result.data);\n setIsLoading(false);\n }).catch(error => {\n toast.error(apiErrorMessage);\n console.error(error);\n setIsLoading(false);\n });\n }, [apiErrorMessage, countryUpdated]);\n\n function editCountry(country: CountryModel): void {\n setCountryUnderEdit(country);\n setShowEditCountryDialog(true);\n };\n\n return (\n <>\n\n {\n showEditCountryDialog &&\n setShowEditCountryDialog(false)}\n country={countryUnderEdit}\n setCountryUnderEdit={setCountryUnderEdit}\n setCountryUpdated={setCountryUpdated}\n />\n }\n\n {\n isLoading && \n }\n\n {\n !isLoading &&\n \n \n t(countryRow.resultType)\n },\n {\n title: t(\"countries-ui.countries-table.header-is-visible-to-public\"),\n field: \"isVisibleToPublic\",\n render: countryRow => countryRow.isVisibleToPublic ? t(\"yes\") : t(\"no\")\n }\n ]}\n data={countryRows}\n actions={[\n countryRow => ({\n icon: () => ,\n tooltip: countryRow.canEdit\n ? t('countries-ui.countries-table.edit-button-tootip-default', { countryName: countryRow.name })\n : t('countries-ui.countries-table.edit-button-tootip-denied', { countryName: countryRow.name }),\n onClick: (_event: any, data: any): void => editCountry(data),\n disabled: !countryRow.canEdit\n })\n ]}\n options={{\n actionsColumnIndex: -1,\n search: false,\n sorting: false,\n paging: false,\n toolbar: false,\n draggable: false\n }}\n />\n \n \n }\n >\n );\n};\n","import { Box } from '@material-ui/core';\nimport React from 'react';\nimport { Trans } from 'react-i18next';\n\nimport ManageCountriesTable from './ManageCountriesTable';\n\nexport const CountryList = () => {\n\n return (\n \n \n Countries \n \n\n \n\n \n );\n}\n","import React from 'react';\nimport { AppBar, Tabs, Tab, Box, Grid } from '@material-ui/core';\nimport MasterDataDownload from '../MasterData/MasterDataDownload';\nimport MasterDataUpload from '../MasterData/MasterDataUpload';\nimport CalculationsExport from '../CalculationsExport/CalculationsExport';\nimport NewsItems from '../NewsItems/NewsItems';\nimport { useTranslation } from 'react-i18next';\nimport { CountryList } from '../Countries/CountryList';\n\ninterface TabPanelProps {\n children?: React.ReactNode;\n index: any;\n value: any;\n}\n\nfunction TabPanel(props: TabPanelProps) {\n const { children, value, index, ...other } = props;\n\n return (\n \n {value === index && (\n \n {children}\n \n )}\n
\n );\n}\n\nfunction a11yProps(index: any) {\n return {\n id: `simple-tab-${index}`,\n 'aria-controls': `simple-tabpanel-${index}`,\n };\n}\n\nexport default function SiteTabs() {\n\n const { t } = useTranslation();\n\n const [value, setValue] = React.useState(0);\n\n const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {\n setValue(newValue);\n };\n\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n}\n","import React from 'react';\nimport { Button } from '@material-ui/core';\nimport { useTranslation } from 'react-i18next';\nimport { AzureAD, AuthenticationState } from 'react-aad-msal';\nimport { getAuthProvider } from '../../authentication/authProvider';\nimport { store } from '../../store/store';\nimport SiteTabs from './SiteTabs';\n\nexport function LandingPage() {\n \n const handleLogin = (azureADLogin: () => {}) => {\n azureADLogin();\n console.log('User will be redirected to Azure AD Login');\n };\n\n const { t } = useTranslation();\n\n return (\n \n
\n {\n ({ login, logout, authenticationState, error, accountInfo}: any) => {\n switch (authenticationState) {\n case AuthenticationState.Authenticated:\n return ;\n \n case AuthenticationState.Unauthenticated:\n return (\n \n
{t('landing-page.unauthenticated-title')} \n
{t('landing-page.subtitle')}
\n
handleLogin(login)}>\n {t('landing-page.login-button')}\n \n
\n );\n case AuthenticationState.InProgress:\n return (\n \n Authenticating...\n
\n );\n default:\n console.log('Authentication Error', error);\n return (\n \n Authentication Error\n
\n );\n }\n }\n }\n \n
\n );\n}\n","import React, { Suspense } from 'react';\nimport { Provider } from 'react-redux';\nimport { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';\nimport { CssBaseline } from '@material-ui/core';\nimport { ThemeProvider } from '@material-ui/core/styles';\nimport { ToastContainer } from 'react-toastify';\nimport 'react-toastify/dist/ReactToastify.min.css';\n\nimport { Layout } from '../Layout/Layout';\nimport { store } from '../../store/store';\nimport { theme } from '../../theme/theme';\n\nimport { LandingPage } from '../LandingPage/LandingPage';\n\nimport logo from '../../assets/images/kingspan-logo.svg';\n\nconst Loader = () => (\n \n
\n
loading...
\n
\n);\n\nexport function App() {\n return (\n \n \n \n }>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n}\n","import i18n from 'i18next';\nimport Backend from 'i18next-http-backend';\nimport LanguageDetector from 'i18next-browser-languagedetector';\nimport { initReactI18next } from 'react-i18next';\n\ni18n\n // load translation using http -> see /public/locales\n // learn more: https://github.com/i18next/i18next-http-backend\n .use(Backend)\n // detect user language\n // learn more: https://github.com/i18next/i18next-browser-languageDetector\n .use(LanguageDetector)\n // pass the i18n instance to react-i18next.\n .use(initReactI18next)\n // init i18next\n // for all options read: https://www.i18next.com/overview/configuration-options\n .init({\n fallbackLng: 'en',\n debug: false,\n\n interpolation: {\n escapeValue: false, // not needed for react as it escapes by default\n },\n });\n\nexport default i18n;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport { App } from './components/App/App';\nimport { setAppConfig } from './config/config';\nimport { AppConfig } from './types/config/configTypes';\n\nimport './i18n';\nimport axios from 'axios';\n\n(async function() {\n try {\n const clientConfig = await (await fetch('/config.json')).json();\n const { apiUrl } = clientConfig;\n const configData = await fetch(`${apiUrl}/config.json`);\n const appConfig = {\n ...(await configData.json()) as AppConfig,\n ...clientConfig\n };\n\n console.log('appConfig', appConfig);\n\n setAppConfig(appConfig);\n\n // Set the base URL for the http client\n axios.defaults.baseURL = apiUrl;\n\n ReactDOM.render( , document.getElementById('root'));\n } catch (e) {\n console.error('App config could not be loaded');\n console.error(e);\n }\n})();\n"],"sourceRoot":""}