\n );\n};\n","import React from \"react\";\nimport { Button } from \"../FormElements/Button/Button.js\";\n\nexport const Contact = (props) => {\n return (\n
\n
\n \n
\n \n\n
\n Datapharm runs emc (electronic medicines compendium), the UK’s\n leading provider of trusted medical information, and provides\n regulatory and compliance software solutions to the\n pharmaceutical industry.\n {\n window.open(\"https://www.datapharm.com/\", \"_blank\");\n }}\n >\n More About Us\n \n
\n \n
\n You can contact with us if you have questions and problems with\n your account or if you need help about emc InDemand.\n
{\n if (onClick) {\n onClick();\n }\n }}\n onKeyPress={(e) => {\n if ((e.key = \"Enter\")) {\n onClick();\n }\n }}\n >\n
\n Contact\n \n
\n
\n \n
\n
\n );\n};\n","import React, { useContext, useState, useCallback } from \"react\";\nimport { useHistory } from \"react-router-dom\";\nimport _ from \"lodash\";\nimport { GlobalContext } from \"contexts/GlobalStore\";\nimport { UserContext } from \"contexts/UserStore.js\";\nimport { BackEmblem } from \"../BackEmblem/BackEmblem.js\";\nimport { CreateNew } from \"./CreateNew.js\";\nimport { SelectStatus } from \"./SelectStatus.js\";\nimport { SelectType } from \"./SelectType.js\";\nimport { UserSettings } from \"./UserSettings.js\";\nimport { Contact } from \"./Contact.js\";\nimport { NavButton } from \"./NavButton.js\";\nimport { NavButtonMD } from \"./NavButtonMD.js\";\nimport { NavButtonContact } from \"./NavButtonContact.js\";\nimport \"./Sidebar.scss\";\n\nexport const Sidebar = (props) => {\n const history = useHistory();\n const { sidebarBehavior, setSidebarBehavior, pageName, goToHome } = useContext(GlobalContext);\n const { userState, isAuthenticated } = useContext(UserContext);\n const [disableOnMouseLeave, setDisableOnMouseLeave] = useState(false);\n\n /**\n * HANDLE CONTENT\n * @description changes sidebar behaviour in global store\n * @param {string} newContent\n */\n const handleContent = (newContent) => {\n if (newContent === sidebarBehavior.content) {\n return;\n }\n setDisableOnMouseLeave(true);\n setSidebarBehavior((prev) => ({\n ...prev,\n content: newContent,\n }));\n delayDisableMouseLeave();\n };\n\n const delayDisableMouseLeave = useCallback(\n _.debounce(async (obj) => {\n setDisableOnMouseLeave(false);\n }, 250),\n [],\n );\n\n /**\n * ON MOUSE LEAVE FROM SIDEBAR\n * @description if mouse leave over sidebar, sidebar has to turn condensed version\n * @description An additional function can be added here to block onMouseOver immediately. It may help to user.\n */\n const onMouseLeaveAction = () => {\n if (disableOnMouseLeave || sidebarBehavior.position === \"mobile\") {\n return;\n }\n\n if (\n sidebarBehavior.position !== \"narrow\" &&\n sidebarBehavior.position !== \"landing\" &&\n pageName !== \"homePage\"\n ) {\n return;\n } else if (pageName === \"aboutPage\" && sidebarBehavior.content !== \"about\") {\n handleContent(\"about\");\n } else if (\n ((pageName && pageName.split(\"/\")[0] === \"handlePage\") ||\n pageName === \"listingPage\" ||\n pageName === \"homePage\") &&\n sidebarBehavior.content !== \"none\"\n ) {\n handleContent(\"none\");\n }\n };\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n\n if (!isAuthenticated()) {\n return null;\n }\n\n return (\n
{\n onMouseLeaveAction();\n }}\n >\n {/* CLOSE X ICON FOR MOBILE MENU */}\n {\n setSidebarBehavior({ position: \"mobile\", content: \"hide\" });\n }}\n />\n\n {/* VERTICAL LINE */}\n \n\n {/* SIDEBAR SUB CONTENTS -- for Select Status for Select Type for User Settings or for Contact */}\n
\n \n \n \n \n \n
\n\n {/* BACKEMBLEM */}\n \n\n {/* SIDEBAR HEADERZONE STARTED */}\n
\n {/* LOGOS */}\n
\n {\n goToHome();\n }}\n src={require(\"../../img/logoNegative.svg\")}\n alt=\"emc med data dm+d\"\n /> \n {\n goToHome();\n }}\n src={require(\"../../img/logoNegative_iconSize.svg\")}\n alt=\"emc med data dm+d\"\n /> \n
\n \n \n );\n }\n //-------------------------------------------------------------\n // \t\tDEFAULT RETURN\n //-------------------------------------------------------------\n else {\n return null;\n }\n};\n","import React, { useContext } from \"react\";\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\n\nimport { Link } from \"react-router-dom\";\nimport \"./Card.scss\";\n\nexport const Card = (props) => {\n const {\n hideStatus,\n id,\n type,\n status,\n medName,\n dateOn,\n dateBy,\n nhsMsg,\n //submissionId,\n additionalStyle,\n } = props;\n const { statusLib, addSpaces } = useContext(GlobalContext);\n const statusCSS = () => {\n switch (status) {\n case statusLib.DRA.co:\n case statusLib.DRA.scr:\n return \"draft\";\n case statusLib.RFA.co:\n case statusLib.RFA.scr:\n return \"ready\";\n case statusLib.NHS.co:\n case statusLib.NHS.scr:\n return \"withNHS\";\n case statusLib.CON.co:\n case statusLib.CON.scr:\n return \"confirmed\";\n case statusLib.REJ.co:\n case statusLib.REJ.scr:\n return \"rejected\";\n case statusLib.CAN.co:\n case statusLib.CAN.scr:\n return \"canceled\";\n case statusLib.PENSUB.co:\n case statusLib.PENSUB.scr:\n return \"pending\";\n case statusLib.PENCAN.co:\n case statusLib.PENCAN.scr:\n return \"pending\";\n default:\n return \"draft\";\n }\n };\n\n return (\n \n
\n
\n \n
{status}
\n\n \n
{addSpaces(type)}
\n
{medName}
\n \n
\n {status === statusLib.DRA.co ||\n status === statusLib.DRA.scr ||\n status === statusLib.RFA.co ||\n status === statusLib.RFA.scr ||\n status === statusLib.CAN.co ||\n status === statusLib.CAN.scr ? (\n
\n Last Updated on {dateOn}\n \n by {dateBy}\n
\n ) : status === statusLib.NHS.co ||\n status === statusLib.NHS.scr ||\n status === statusLib.PENSUB.co ||\n status === statusLib.PENSUB.scr ||\n status === statusLib.PENCAN.co ||\n status === statusLib.PENCAN.scr ? (\n
\n Authorised on {dateOn}\n \n by {dateBy}\n
\n ) : status === statusLib.CON.co ||\n status === statusLib.REJ.co ||\n status === statusLib.CON.scr ||\n status === statusLib.REJ.scr ? (\n
{nhsMsg}
\n ) : null}\n
\n \n );\n};\n","import React from \"react\";\nimport { useHistory } from \"react-router-dom\";\n\nexport const CardSeeAll = (props) => {\n const { howMuchMore, status } = props;\n let history = useHistory();\n\n return (\n
{\n history.push({\n pathname: \"/list\",\n search:\n \"?\" +\n new URLSearchParams({ status: status }).toString(),\n });\n }}\n >\n
\n Welcome to emc med data dm+d. You haven't yet made any submissions to the NHS\n BSA. Once started, your submissions will appear here. Please Create a New\n Submission using the menu option\n
\n
\n
\n );\n }\n return (\n \n {/* SEARCH BAR NOT IMPLEMENTED IN THIS VERSION - REMOVE - packer in to make ui look ok*/}\n {/*
\n );\n};\n","import React from \"react\";\nimport useEventListener from \"@use-it/event-listener\";\n\nimport { Button } from \"../Button/Button.js\";\nimport \"./ApprovalZone.scss\";\n\nexport const ApprovalZone = (props) => {\n const {\n onConfirm,\n onDecline,\n headerText,\n redText,\n onConfirmText,\n onDeclineText,\n areYouSureText,\n } = props;\n\n /**\n * EVENT LISTENER FOR ENTER or ESC\n * @description onConfirm when Enter pressed - onDecline when Esc pressed\n */\n useEventListener(\"keydown\", (e) => {\n if (e.keyCode === 13 || e.keyCode === 32) {\n e.preventDefault();\n onConfirm();\n } else if (e.keyCode === 27) {\n e.preventDefault();\n onDecline();\n }\n\n return;\n });\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n\n if (!onConfirm || !onDecline || !headerText || !redText) {\n return null;\n }\n return (\n
\n
\n
{headerText}
\n
{redText}
\n
{areYouSureText ? areYouSureText : \"Are you sure?\"}
\n
\n
\n
\n
\n );\n};\n","import React, { useLayoutEffect, useState, useContext } from \"react\";\nimport { Prompt } from \"react-router\";\nimport { useHistory } from \"react-router-dom\";\nimport useEventListener from \"@use-it/event-listener\";\nimport _ from \"lodash\";\n\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\nimport { UserContext } from \"contexts/UserStore.js\";\nimport { Button } from \"components/FormElements/Button/Button.js\";\nimport { Seperator } from \"components/FormElements/Seperator/Seperator.js\";\nimport { ApprovalZone } from \"components/FormElements/ApprovalZone/ApprovalZone.js\";\nimport { FetchAPI } from \"components/APIConnections/APIConnections.js\";\nimport { IsLoading } from \"components/IsLoading/IsLoading.js\";\n\nexport const UserActions = (props) => {\n const {\n data,\n errorFieldNameClicked,\n settings,\n setSettings,\n sections,\n convertCurrentValueToSavedValue,\n silentUpdateData,\n areTheyEqual,\n } = props;\n const {\n ID,\n action,\n status,\n submissionType,\n submissionID,\n internalState,\n isThereAnyChange,\n } = settings;\n const {\n isNotEmpty,\n callSaveAndContinue,\n callPreSection,\n callNextSection,\n headerTitles,\n triggerGlobalAlert,\n showErrorLogsOnConsole,\n } = useContext(GlobalContext);\n const { userState } = useContext(UserContext);\n const [allErrorFields, setAllErrorFields] = useState([]);\n let history = useHistory();\n\n const [propsForApprovalState, setPropsForApprovalState] = useState();\n const [localIsLoading, setLocalIsLoading] = useState(false);\n const [firstRender, setFirstRender] = useState(true);\n const [promptWhen, setPromptWhen] = useState(true);\n\n /**\n * FIND ALL ERROR FIELDS\n */\n useLayoutEffect(() => {\n if (!data) {\n return;\n }\n let newAllErrorFields = [];\n\n Object.entries(data).map(([key, item]) => {\n if (\n (action !== \"view\" && (item.internalState || 1) > (internalState || 1)) ||\n item.dontSendToApi\n ) {\n return null;\n }\n\n if (item.errorText && item.errorText !== [] && item.errorText.length > 0) {\n newAllErrorFields.push(key);\n }\n return null;\n });\n\n setAllErrorFields(newAllErrorFields);\n }, [data, internalState, action]);\n\n /**\n * EVENT LISTENER FOR CTRL + S - AND ARROW KEYS\n * @description ctrl S is triggering save as draft -> saveAndContinue in UserActions\n */\n useEventListener(\"keydown\", (e) => {\n if (action === \"view\") {\n return;\n }\n\n const preventDef = (e) => {\n if (e.preventDefault) {\n e.preventDefault();\n }\n };\n\n let isCtrlKeyDown = navigator.platform.indexOf(\"Mac\") > -1 ? e.metaKey : e.ctrlKey;\n let isSDown = (e.key && e.key === \"s\") || (e.keyCode || e.which) === 83;\n\n if (isCtrlKeyDown && isSDown) {\n preventDef(e);\n saveAndContinue();\n return false;\n }\n });\n\n /**\n * DISABLE or ENABLE SCROLLING DEPENDING on APPROVAL STATEs\n */\n useLayoutEffect(() => {\n if (propsForApprovalState || localIsLoading) {\n // scroll disabled\n document.getElementsByTagName(\"body\")[0].setAttribute(\"style\", \"overflow-y:hidden;\");\n } else {\n // scroll enabled\n document.getElementsByTagName(\"body\")[0].setAttribute(\"style\", \"overflow-y:auto;\");\n }\n }, [propsForApprovalState, localIsLoading]);\n\n /**\n * UNMOUNT EFFECT FOR ENABLE SCROLLING.\n * @description need this, because on page switch, scrolling left disabled without this correction.\n */\n useLayoutEffect(() => {\n setPromptWhen(true);\n return () => {\n setLocalIsLoading(false);\n setPropsForApprovalState();\n document.getElementsByTagName(\"body\")[0].setAttribute(\"style\", \"overflow-y:auto;\");\n };\n }, []);\n\n /**\n * REMOVE FIRST RENDER\n */\n useLayoutEffect(() => {\n setFirstRender(false);\n }, []);\n\n /**\n * HANDLE GO TO TOP\n * @description Goes to top of the page smoothly.\n */\n const handleGoToTop = () => {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n };\n\n /**\n * LISTEN JUMP TO SECTION\n */\n const jumpToSection = (preOrNext) => {\n if (\n (submissionType === \"UpdateProduct\" && internalState < 3) ||\n (submissionType === \"UpdatePack\" && internalState < 4)\n ) {\n return;\n }\n\n let scrollY = Math.round(\n window.scrollY ||\n window.scrollTop ||\n document.getElementsByTagName(\"html\")[0].scrollTop,\n );\n\n let allSectionYValues = [];\n sections.forEach((item) => {\n allSectionYValues.push(document.getElementById(item).offsetTop);\n });\n allSectionYValues.sort(function (a, b) {\n return a - b;\n });\n\n const arraySiraliEkle = (array, elem) => {\n let i = 0;\n while (i < array.length && array[i] < elem) {\n i++;\n }\n return i;\n };\n\n let i = arraySiraliEkle(allSectionYValues, scrollY);\n\n let prePoint = allSectionYValues[i - 1];\n if (prePoint === scrollY || (prePoint < scrollY + 10 && prePoint > scrollY - 10)) {\n prePoint = allSectionYValues[i - 2];\n }\n if (!prePoint) {\n prePoint = 0;\n }\n\n let nextPoint = allSectionYValues[i];\n if (nextPoint === scrollY || (nextPoint < scrollY + 10 && nextPoint > scrollY - 10)) {\n nextPoint = allSectionYValues[i + 1];\n }\n if (!nextPoint) {\n nextPoint = document.body.scrollHeight;\n }\n\n if (preOrNext === \"pre\") {\n window.scrollTo({ top: prePoint, left: 0, behavior: \"smooth\" });\n } else if (preOrNext === \"next\") {\n window.scrollTo({ top: nextPoint, left: 0, behavior: \"smooth\" });\n }\n };\n\n /**\n * CHANGE STATUS\n * @description change submission status from Draft to \"ReadyForAuthorisation\" or \"PendingSubmission\" or \"Cancelled\"\n * This works, if the user editing the page.\n * Otherwise, if user creating a new page, (action===\"new\") mark button directs user to SaveAndGoHome function.\n */\n const changeStatus = async (newStatus) => {\n if (!newStatus) {\n return;\n }\n let serverReply;\n\n if (newStatus === \"cancel\") {\n serverReply = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n callbackWithErrorStatusOrConfirmation: true,\n history: history,\n apiShortName: \"markAsCancelled\",\n token: userState.accessToken,\n apiNeeds: {\n ID: ID,\n submissionID: submissionID,\n companyId: userState.selectedCompany?.id,\n },\n setIsLoading: (boo) => {\n setLocalIsLoading(boo);\n },\n });\n } else if (newStatus === \"rfa\") {\n serverReply = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n callbackWithErrorStatusOrConfirmation: true,\n history: history,\n apiShortName: \"updateASubmission\",\n token: userState.accessToken,\n apiNeeds: {\n companyId: userState.selectedCompany?.id,\n ID: ID,\n submissionID: submissionID,\n name: settings.name,\n status: \"ReadyForAuthorisation\",\n },\n setIsLoading: (boo) => {\n setLocalIsLoading(boo);\n },\n });\n } else if (newStatus === \"nhs\") {\n serverReply = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n callbackWithErrorStatusOrConfirmation: true,\n history: history,\n apiShortName: \"updateASubmission\",\n token: userState.accessToken,\n apiNeeds: {\n companyId: userState.selectedCompany?.id,\n ID: ID,\n submissionID: submissionID,\n name: settings.name,\n status: \"PendingSubmission\",\n },\n setIsLoading: (boo) => {\n setLocalIsLoading(boo);\n },\n });\n } else {\n history.push({ pathname: \"/error/missingStatusName\" });\n }\n\n if (serverReply.ok === false || serverReply.id === undefined) {\n // !permissions.ok gives an error because undefined is not a false in BE side.\n setLocalIsLoading(false);\n setPropsForApprovalState({\n pathName: \"/\",\n headerText: \"A server error has occurred (\" + serverReply.status + \")\",\n redText: \"Your submission has not been successful. \",\n areYouSureText: \"Would you like to try again? \",\n onConfirmText: \"Yes\",\n onConfirm: () => {\n setPropsForApprovalState();\n },\n onDeclineText: \"No\",\n onDecline: () => {\n history.push({ pathname: \"/\" });\n },\n });\n } else {\n setPromptWhen(false);\n history.push({ pathname: \"/\" });\n }\n };\n\n /**\n * CREATE THE API PACKAGE for CURRENT VALUES and POST IT\n * @description updates store depending on changes via fields.\n */\n const createApiPackage = async () => {\n if (!data) {\n return false;\n }\n let newData = _.cloneDeep(data);\n\n /**\n * CHECK IF FILE IS UPLOADED or NOT\n * Then, upload if it's not uploaded yet.\n */\n if (\n (submissionType === \"NewProduct\" ||\n submissionType === \"NewPack\" ||\n submissionType === \"UpdateProduct\" ||\n submissionType === \"UpdatePack\") &&\n data.smpcFile?.currentValue?.fileProps &&\n !data.smpcFile?.currentValue?.fileUrl\n ) {\n let serverReply = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n callbackWithErrorStatusOrConfirmation: true,\n history: history,\n apiShortName: \"fileUpload\",\n token: userState.accessToken,\n apiNeeds: {\n fileProps: data.smpcFile.currentValue.fileProps[0],\n },\n });\n\n let URL = serverReply.uri;\n\n newData.smpcFile.currentValue.fileUrl = URL;\n silentUpdateData(\"smpcFile\", {\n ...data.smpcFile.currentValue,\n fileUrl: URL,\n });\n }\n\n /**\n * CREATE API PACKAGE\n */\n let apiPackage = {};\n\n Object.keys(newData).forEach((item) => {\n if (\n !newData[item].dontSendToApi &&\n isNotEmpty(newData[item].currentValue) &&\n (newData[item].internalState || 1) <= (internalState || 1)\n ) {\n apiPackage[item] = convertCurrentValueToSavedValue(\n newData[item],\n newData[item].currentValue,\n );\n }\n });\n\n if (Object.keys(apiPackage).length === 0) {\n return false;\n }\n\n /**\n * @description NewPack doesn't have name field. But Api needs it.\n * In here, I'm creating a name field for newPack and for other forms as well, if their name field is empty.\n * Because validations are not blocking for Draft Saving.\n */\n if (!apiPackage.name) {\n apiPackage.name = headerTitles.productName || \"No Name\";\n }\n /**\n * UpdateProduct and UpdatePack needs some extra fields depending on discontinue product or pack values.\n */\n if (submissionType === \"UpdateProduct\" || submissionType === \"UpdatePack\") {\n if (\n (data.discontinueProduct || data.discontinuePack) &&\n (data.discontinueProduct?.currentValue?.label === \"Yes\" ||\n data.discontinuePack?.currentValue?.label === \"Yes\")\n ) {\n apiPackage.discontinued = true;\n }\n }\n /**\n * Add Single Price update here --->\n */\n if (submissionType === \"UpdatePack\") {\n apiPackage.singlePriceUpdate = false;\n\n if (settings.inComingPack && data.price) {\n const relatedFields_isItSinglePriceSub = [\n \"quantity\",\n \"legalCategory\",\n \"subpackInfo\",\n \"gtinCodes\",\n \"calendarPack\",\n \"hospitalPack\",\n \"limitedStability\",\n ];\n\n if (\n !areTheyEqual(settings.inComingPack.price.currentValue, data.price.currentValue)\n ) {\n apiPackage.singlePriceUpdate = true;\n\n for (let i = 0; i < relatedFields_isItSinglePriceSub.length; i++) {\n let item = relatedFields_isItSinglePriceSub[i];\n if (\n !areTheyEqual(\n settings.inComingPack[item].currentValue,\n data[item].currentValue,\n )\n ) {\n apiPackage.singlePriceUpdate = false;\n break;\n }\n }\n }\n }\n }\n\n /**\n * remove 0 items from the list\n */\n if (submissionType === \"MassPriceUpdate\") {\n apiPackage[\"prices\"] = apiPackage[\"prices\"].filter(item => item.newPrice > 0);\n const priceLength = apiPackage[\"prices\"].length;\n\n if(priceLength === 0)\n return null;\n\n apiPackage[\"name\"] = apiPackage[\"name\"].replace(/(\\d+)/g, priceLength);\n }\n\n return apiPackage;\n };\n\n /**\n * POST SUBMISSION is a assistant function.\n * Is called by SaveAndContinue or SaveAndGoHome functions.\n */\n const postSubmission = async (apiPackage, newStatus = \"draft\", disableIsLoading = false) => {\n let newStatusName;\n if (newStatus === \"draft\") {\n newStatusName = \"Draft\";\n } else if (newStatus === \"rfa\") {\n newStatusName = \"ReadyForAuthorisation\";\n } else if (newStatus === \"nhs\") {\n newStatusName = \"PendingSubmission\";\n } else if (newStatus === \"cancel\") {\n newStatusName = \"Cancelled\";\n }\n\n /**\n * CREATE SUBMISSION DEPENDING ON REQUEST\n */\n let serverReply = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n callbackWithErrorStatusOrConfirmation: true,\n history: history,\n apiShortName: action === \"new\" ? \"postNewSubmission\" : \"updateASubmission\",\n token: userState.accessToken,\n apiNeeds: {\n title: apiPackage.name,\n status: newStatusName, // --> Draft RFA or pending submission -> CHANGE HERE.\n type: submissionType,\n companyId: userState.selectedCompany?.id,\n ...(action === \"edit\" && { ID: ID, submissionID: submissionID }),\n data: {\n ...apiPackage,\n },\n },\n });\n\n return serverReply;\n };\n const saveAndGoHome = async (newStatus) => {\n let apiPackage = await createApiPackage();\n if (!apiPackage) {\n return;\n }\n\n setLocalIsLoading(true);\n\n let serverReply = await postSubmission(apiPackage, newStatus);\n\n // GO HOME AFTER SAVING\n\n if (serverReply.ok === false || serverReply.id === undefined) {\n let headerText = \"A server error has occurred (\" + serverReply.status + \")\";\n let redText = \"Your data has not been saved.\";\n\n if(serverReply.status === 400) {\n headerText = \"Invalid Data\";\n redText = await serverReply.text();\n }\n\n setLocalIsLoading(false);\n setPropsForApprovalState({\n pathName: \"/\",\n headerText: headerText,\n redText: redText,\n areYouSureText: \"Would you like to try again? \",\n onConfirmText: \"Yes\",\n onConfirm: () => {\n setPropsForApprovalState();\n }, \n onDeclineText: \"No\",\n onDecline: () => {\n history.push({ pathname: \"/\" });\n },\n });\n } else {\n setLocalIsLoading(false);\n setPromptWhen(false);\n history.push({ pathname: \"/\" });\n }\n };\n const saveAndContinue = async () => {\n if (!isThereAnyChange) {\n return;\n }\n let apiPackage = await createApiPackage();\n if (!apiPackage) {\n return;\n }\n\n if (showErrorLogsOnConsole) {\n console.log(apiPackage);\n }\n\n let serverReply = await postSubmission(apiPackage, \"draft\", false);\n\n if (serverReply.ok === false || serverReply.id === undefined) {\n triggerGlobalAlert({\n text: \"Your data has not been saved. (\" + serverReply.status + \")\",\n status: \"warn\",\n });\n } else if (action === \"new\") {\n setPromptWhen(false);\n history.push(\"/edit/\" + serverReply.id);\n } else {\n triggerGlobalAlert({\n text: \"Saved successfully.\",\n });\n setSettings({ ...settings, isThereAnyChange: false });\n }\n };\n\n useLayoutEffect(() => {\n if (firstRender) {\n return;\n }\n saveAndContinue();\n }, [callSaveAndContinue]);\n useLayoutEffect(() => {\n if (firstRender) {\n return;\n }\n jumpToSection(\"pre\");\n }, [callPreSection]);\n useLayoutEffect(() => {\n if (firstRender) {\n return;\n }\n jumpToSection(\"next\");\n }, [callNextSection]);\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n\n return (\n \n {/* REF POINT */}\n \n\n {/* PROMPT */}\n {\n if (\n action === \"view\" ||\n !isThereAnyChange ||\n (propsForApprovalState &&\n location.pathname === propsForApprovalState.pathName)\n ) {\n setPropsForApprovalState();\n return true;\n } else {\n setPropsForApprovalState({\n pathName: location.pathname,\n headerText: \"Do You Want To Leave?\",\n redText: \"You may lose changes on form.\",\n onConfirm: () => {\n history.push(location.pathname);\n },\n onDecline: () => {\n setPropsForApprovalState();\n },\n });\n return false;\n }\n }}\n />\n\n {/* INTERNAL USAGE FOR ISLOADING */}\n \n\n {/* APPROVAL STAGE */}\n {!propsForApprovalState ? null : (\n \n )}\n\n {/* ACTIONS SEPERATOR */}\n {status === \"Cancelled\" ? null : }\n\n {/* SAVE AS DRAFT - DISCARD CHANGES */}\n {action === \"view\" || status !== \"Draft\" ? null : (\n \n {\n saveAndGoHome(\"draft\");\n }}\n />\n {\n setPropsForApprovalState({\n pathName: \"/\",\n headerText: \"Discard Changes Confirmation\",\n redText: \"Any changes made may be lost.\",\n onConfirm: () => {\n history.push(\"/\");\n },\n onDecline: () => {\n setPropsForApprovalState();\n },\n });\n }}\n additionalStyle={{ marginRight: 0 }}\n />\n \n )}\n\n {/* VALIDATION ERRORS BOX for VIEW+DRAFT */}\n {action === \"view\" &&\n status === \"Draft\" &&\n allErrorFields &&\n allErrorFields.length > 0 ? (\n
\n You have missing fields in the form. For marking the submission as \"Ready to\n Authorisation\" or for submitting submission to NHS, please edit and fill all\n mandatory fields.\n
\n You have missing fields in the form. For marking the submission as \"Ready to\n Authorisation\" or for submitting submission to NHS, please correct these fields:\n \n \n {allErrorFields.map((error) => {\n let errorFieldName = data[error].fieldName;\n\n return (\n {\n errorFieldNameClicked(error);\n }}\n >\n {errorFieldName}\n \n );\n })}\n
\n ) : null}\n\n {/* EDIT SUBMISSION BUTTON */}\n\n {action === \"view\" && status === \"Draft\" ? (\n {\n history.push(\"/edit/\" + ID);\n }}\n />\n ) : null}\n\n {/* MARK AS READY FOR AUTH. BUTTON */}\n {(!allErrorFields || allErrorFields.length === 0) &&\n userState.currentPermLevel === 1 &&\n status === \"Draft\" ? (\n {\n setPropsForApprovalState({\n pathName: null,\n headerText: \"Mark As Ready Confirmation\",\n redText: \"You are about to change your submission's status.\",\n onConfirm: () => {\n if (action === \"view\") {\n changeStatus(\"rfa\");\n } else {\n saveAndGoHome(\"rfa\");\n }\n setPropsForApprovalState();\n },\n onDecline: () => {\n setPropsForApprovalState();\n },\n });\n }}\n />\n ) : null}\n\n {/* SUBMIT TO NHS BUTTON */}\n {(!allErrorFields || allErrorFields.length === 0) &&\n userState.currentPermLevel === 2 &&\n (status === \"Draft\" || status === \"ReadyForAuthorisation\") ? (\n {\n setPropsForApprovalState({\n pathName: null,\n headerText: \"Submit Submission to NHS BSA\",\n redText: \"You are about to change your submission's status.\",\n onConfirm: () => {\n if (action === \"view\") {\n changeStatus(\"nhs\");\n } else {\n saveAndGoHome(\"nhs\");\n }\n setPropsForApprovalState();\n },\n onDecline: () => {\n setPropsForApprovalState();\n },\n });\n }}\n />\n ) : null}\n\n {/* MARK AS CANCELLED BUTTON */}\n {(status !== \"Draft\" &&\n status !== \"ReadyForAuthorisation\" &&\n status !== \"PendingSubmission\" &&\n status !== \"WithNhsBsa\") ||\n action === \"new\" ? null : (\n {\n setPropsForApprovalState({\n pathName: null,\n headerText: \"Cancellation Confirmation\",\n redText: \"Once confirmed, this cannot be undone.\",\n onConfirm: () => {\n changeStatus(\"cancel\");\n setPropsForApprovalState();\n },\n onDecline: () => {\n setPropsForApprovalState();\n },\n });\n }}\n />\n )}\n\n {/* PRINT - EXPORT - TOP */}\n
\n To request an ingredient be added to the dm+d please click here. Once the ingredient has been added to the dm+d, you will be able to make your submission to the NHS BSA\n
\n );\n }\n};\n","import _ from \"lodash\";\n\n/**\n * hasToBeFilled\n * @description Mandotory Check\n * @returns true || errorText\n */\nexport const hasToBeFilled = (props) => {\n let errorText = \"Field has to be filled.\";\n const { DATA, typeIdentifier } = props;\n if (!DATA) {\n return errorText;\n }\n let typeOfData = typeIdentifier(DATA);\n\n if (typeOfData === \"array\" || typeOfData === \"string\") {\n if (DATA.length > 0) {\n return true;\n } else {\n return errorText;\n }\n } else if (typeOfData === \"object\") {\n if (Object.keys(DATA).length > 0) {\n return true;\n } else {\n return errorText;\n }\n }\n\n return false;\n};\n\n/**\n * hasToBeFilled\n * @description Mandotory Check\n * @returns true || errorText\n */\nexport const hasToBeFilledIfVisible = (props) => {\n let errorText = \"Field has to be filled.\";\n const { DATA, typeIdentifier, hideFromUI } = props;\n\n if (hideFromUI) {\n return true;\n }\n\n if (!DATA) {\n return errorText;\n }\n let typeOfData = typeIdentifier(DATA);\n\n if (typeOfData === \"array\" || typeOfData === \"string\") {\n if (DATA.length > 0) {\n return true;\n } else {\n return errorText;\n }\n } else if (typeOfData === \"object\") {\n if (Object.keys(DATA).length > 0) {\n return true;\n } else {\n return errorText;\n }\n }\n\n return false;\n};\n\n/**\n * hasToBeFilledDate\n * @description Mandotory Check for DATE INPUTS\n * @returns true || errorText\n */\nexport const hasToBeFilledDate = (props) => {\n let errorText = \"Date has to be filled.\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n } else {\n return true;\n }\n};\n\n/**\n * hasToBeTodayOrFuture\n * @description Effective date can not be in the past.\n * @returns true || errorText\n */\nconst today = new Date();\nconst todayDay = today.getDate();\nconst todayMonth = today.getMonth();\nconst todayYear = today.getFullYear();\nexport const hasToBeTodayOrFuture = (props) => {\n let errorText = \"The Date has to be today or in future.\";\n const { DATA } = props;\n\n const prependZero = (value) => {\n return (\"0\" + value).slice(-2);\n };\n\n if (!DATA || _.isEmpty(DATA.year) || _.isEmpty(DATA.month) || _.isEmpty(DATA.day)) {\n return errorText;\n }\n\n let currentData = Number(\n DATA.year.value +\n \"\" +\n prependZero(DATA.month.value) +\n \"\" +\n prependZero(DATA.day.value),\n );\n let todayData = Number(\n todayYear +\n \"\" +\n prependZero(todayMonth + 1) +\n \"\" +\n prependZero(todayDay),\n );\n\n if (currentData < todayData) {\n return errorText;\n }\n\n return true;\n};\n\n/**\n * unithasToBeFilled\n * @description Mandotory Check for unit selector\n * @returns true || errorText\n */\nexport const hasToBeFilledUnit = (props) => {\n let errorText = \"Field has to be filled.\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n\n if (\n Object.keys(DATA).length === 0 ||\n !DATA.input ||\n !DATA.unit ||\n !DATA.unit.value ||\n !DATA.unit.label\n ) {\n return errorText;\n } else {\n return true;\n }\n};\n\n/**\n * unithasToBeFilled\n * @description Mandotory Check for unit selector\n * @returns true || errorText\n */\nexport const hasToBeFilledUnitIfVisible = (props) => {\n let errorText = \"Field has to be filled.\";\n const { DATA, hideFromUI } = props;\n\n if (hideFromUI) {\n return true;\n }\n\n if (!DATA) {\n return errorText;\n }\n\n if (\n Object.keys(DATA).length === 0 ||\n !DATA.input ||\n !DATA.unit ||\n !DATA.unit.value ||\n !DATA.unit.label\n ) {\n return errorText;\n } else {\n return true;\n }\n};\n\n/**\n * hasToBeDocOrPDF\n * @description checks if file type is not a DOC DOCX or PDF\n * @returns true || errorText\n */\nexport const hasToBeDocOrPDF = (props) => {\n let errorText = \"File type has to be doc, docx or pdf\";\n const { DATA } = props;\n\n if (!DATA || (!DATA.fileProps && !DATA.fileUrl)) {\n return errorText;\n } else {\n let fileType;\n if (DATA.fileProps) {\n fileType = DATA.fileProps[0].name.split(\".\");\n } else if (DATA.fileUrl) {\n fileType = DATA.label.split(\".\");\n }\n fileType = fileType[fileType.length - 1].toLowerCase();\n if (fileType === \"doc\" || fileType === \"docx\" || fileType === \"pdf\") {\n return true;\n } else {\n return errorText;\n }\n }\n};\n\n/**\n * hasToBeNumber\n * @description Checks if value is a number or not\n * @returns true || errorText\n */\nexport const hasToBeNumber = (props) => {\n let errorText = \"Value has to be a number\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n\n if (/^-?\\d+$/.test(DATA)) {\n return true;\n } else {\n return errorText;\n }\n};\n\n/**\n * hasToBePrice\n * @description Checks if value is a number or not\n * @returns true || errorText\n */\nexport const hasToBePrice = (props) => {\n let errorText = \"Price must be a whole number (in pence).\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n\n if (/^(?!0+$)[0-9]{1,10}$/.test(DATA)) {\n return true;\n } else {\n return errorText;\n }\n};\n\n/**\n * thereMustBeAnIngAtLeast\n * @description in Ingredients zone, there must be an ing box, minimum\n * @returns true || errorText\n */\nexport const thereMustBeAnIngAtLeast = (props) => {\n let errorText = \"An ingredient has to be added\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n if (Object.keys(DATA).length > 0) {\n return true;\n }\n\n return errorText;\n};\n\n/**\n * thereMustBeAnPackAtLeast\n * @description in Pack Details zone, there must be an pack box, minimum\n * @returns true || errorText\n */\nexport const thereMustBeAnPackAtLeast = (props) => {\n let errorText = \"A pack has to be added\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n if (Object.keys(DATA).length > 0) {\n return true;\n }\n\n return errorText;\n};\n\n/**\n * GTIN CODES CONTROL\n * @description gtin codes has to be 13 or 14 cha long.\n * @returns true || errorText\n */\n export const gtinCodesControl = (props) => {\n let errorText = \"GTIN is invalid. Only valid 13 or 14 digits GTINs are accepted.\";\n const { DATA } = props;\n if (!DATA) {\n return errorText;\n }\n let errorStatus = false;\n\n DATA.forEach((item) => {\n if (!item || item.length < 13 || item.length > 14 || !(/^\\d+$/.test(item))) {\n errorStatus = true;\n }else if(item || item.length === 13 || item.length === 14 || (/^\\d+$/.test(item))){\n var lastDigit = Number(item.substring(item.length - 1));\n var checkSum = 0;\n if (isNaN(lastDigit)) { return false; } // not a valid upc/ean\n \n var arr = item.substring(0,item.length - 1).split(\"\").reverse();\n var oddTotal = 0, evenTotal = 0;\n \n for (var i=0; i {\n const { DATA } = props;\n let newData = [];\n DATA.data.map((item) => {\n newData.push({ label: item.name, value: item.id });\n return null;\n });\n return newData;\n};\n\n/**\n * thereMustBeAPriceUpdateAtLeast\n * @description in Mass Price Update zone, there must be an new price update\n * @returns true || errorText\n */\nexport const thereMustBeAPriceUpdateAtLeast = (props) => {\n let errorText = \"A price update has to be added\";\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n\n if(Object.keys(DATA).filter((key) => typeof(DATA[key].newPrice) !== \"undefined\" && DATA[key].newPrice !== null && DATA[key].newPrice > 0).length > 0) {\n return true;\n }\n\n return errorText;\n};\n\n/**\n * max of 50 price packs\n * @description in Mass Price Update zone, there must be an new price update\n * @returns true || errorText\n */\n export const maxofPriceInput = (props) => {\n let massPriceTopValue = process.env.REACT_APP_MASS_PRICE_TOP_VALUE;\n let errorText = `Maximum input of ${massPriceTopValue} only`;\n const { DATA } = props;\n\n if (!DATA) {\n return errorText;\n }\n if(Object.keys(DATA).filter((key) => typeof(DATA[key].newPrice) !== \"undefined\" && DATA[key].newPrice !== null && DATA[key].newPrice > 0).length <= parseInt(massPriceTopValue)) {\n return true;\n }\n\n return errorText;\n};\n\n/**\n * Unit and Value should both have value\n * @description Mandotory Check for unit selector\n * @returns true || errorText\n */\n export const UnitValueValidation = (props) => {\n let errorText = \"Field has to be filled.\";\n const { DATA } = props;\n\n if (DATA && DATA.input && !DATA.unit ) {\n return errorText;\n }else if(DATA && !DATA.input && DATA.unit){\n return errorText;\n }\n else {\n return true;\n }\n};\n\n/**\n * shouldBeNumber\n * @description Mandotory Check for unit selector\n * @returns true || errorText\n */\n export const shouldBeNumber = (props) => {\n let errorText = \"Input should be number\";\n const { DATA} = props;\n \n if(DATA && DATA.input !== \"\" && !(/^[0-9]*$/.test(DATA.input))) {\n return errorText;\n }\n\n return true;\n};\n/**\n * acceptDecimal\n * @description Mandotory Check for unit selector\n * @returns true || errorText\n */\n export const acceptDecimal = (props) => {\n let errorText = \"Input should be numerical\";\n const { DATA} = props;\n \n if(DATA && DATA.input !== \"\" && !(/^\\d+(\\.\\d{1,10})?$/.test(DATA.input))) {\n return errorText;\n }\n \n return true;\n };\n\n /**\n * isFileSizeWithinRange\n * @description checks the file size if between 1B and 10MB\n * @returns true || errorText\n */\nexport const isFileSizeWithinRange = (props) => {\n let errorText = \"File size must be between 1B and 10MB.\";\n const { DATA } = props;\n\n if (!DATA || (!DATA.fileProps && !DATA.fileUrl)) {\n return errorText;\n }\n\n if(DATA.fileProps && (DATA.fileProps[0].size < 1 || DATA.fileProps[0].size > 10485760)) { // equivalent to 10MB\n return errorText;\n }\n\n return true;\n};","import { Seperator } from \"components/FormElements/Seperator/Seperator.js\";\nimport { Description } from \"components/FormElements/Description/Description.js\";\nimport { DescriptionLink } from \"components/FormElements/DescriptionLink/DescriptionLink.js\";\nimport { TextInput } from \"components/FormElements/TextInput/TextInput.js\";\nimport { MultipleTextInput } from \"components/FormElements/MultipleTextInput/MultipleTextInput.js\";\nimport { SelectBox } from \"components/FormElements/SelectBox/SelectBox.js\";\nimport { MultipleSelectBox } from \"components/FormElements/MultipleSelectBox/MultipleSelectBox.js\";\nimport { DateInput } from \"components/FormElements/DateInput/DateInput.js\";\nimport { DataGrid } from \"components/FormElements/DataGrid/DataGrid.js\";\nimport { UnitSelect } from \"components/FormElements/UnitSelect/UnitSelect.js\";\nimport { RadioButton } from \"components/FormElements/RadioButton/RadioButton.js\";\nimport { FileInput } from \"components/FormElements/FileInput/FileInput.js\";\nimport { Button } from \"components/FormElements/Button/Button.js\";\nimport { Ingredients } from \"components/FormElements/Ingredients/Ingredients.js\";\nimport { Packs } from \"components/FormElements/Packs/Packs.js\";\nimport _ from \"lodash\";\n\nimport {\n hasToBeFilled,\n hasToBeFilledDate,\n hasToBeDocOrPDF,\n thereMustBeAnIngAtLeast,\n thereMustBeAnPackAtLeast,\n handleDataForIngredientApi,\n hasToBeTodayOrFuture,\n hasToBeFilledUnit,\n hasToBePrice,\n gtinCodesControl,\n hasToBeFilledIfVisible,\n hasToBeFilledUnitIfVisible,\n thereMustBeAPriceUpdateAtLeast,\n maxofPriceInput,\n UnitValueValidation,\n shouldBeNumber,\n acceptDecimal,\n isFileSizeWithinRange\n} from \"./validationRules.js\";\nimport {\n yesNoNA,\n yesNo,\n defaultDateOptions,\n trueFalse,\n} from \"./selectBoxOptions.js\";\n\n//-------------------------------------------------------------\n// \t\tNEW PRODUCT\n//-------------------------------------------------------------\n\nexport const NewProduct = ({formulations, licensedRoutes, flavours, controlledDrugCategories, currentLicensing, restrictions, bops, units, legalCategories}) => ({\n seperatorIntro: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Details\",\n },\n name: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Product Name\",\n placeHolder: \"Type Product Name\",\n informationText:\n \"The product name is usually the trade name of the product + strength + form e.g. BrandX 50mg tablets or BrandX 150mg/15ml solution for injection ampoules.\",\n validationRules: [hasToBeFilled],\n },\n relatedVMP: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Related VMP\",\n placeHolder: \"Type to search a VMP\",\n apiNameForOptionValue: \"callVMP\",\n handleData: handleDataForIngredientApi,\n informationText:\"Virtual Medicinal Product (VMP) name is most commonly the generic name + strength + form, e.g. paracetamol 500mg capsule. If the medicine in this form is new to the dm+d, the VMP may not yet exist. Please choose \\\"Not listed\\\" if that is the case.\", \n validationRules: [hasToBeFilled],\n },\n formulation: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Formulation\",\n placeHolder: \"Type and Select\",\n optionValue: formulations,\n informationText: \"Formulation is the dose form of the product e.g. cream, drops, tablet.\",\n validationRules: [hasToBeFilled],\n },\n licensedRoutes: {\n componentTypeName: \"MultipleSelectBox\",\n componentType: MultipleSelectBox,\n fieldName: \"Licensed Routes\",\n placeHolder: \"Type and Select\",\n optionValue: licensedRoutes,\n informationText: \"Select only the licensed route of administration as listed on the SPC.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * EFFECTIVE DATE\n */\n\n seperatorEffectiveDate: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Effective Date\",\n },\n descriptionEffectiveDate: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please enter the effective date for changes. If the date that you choose is within 2 weeks of the current date, please note that the NHS BSA cannot commit to have actioned your request by this date.\",\n },\n effectiveDate: {\n componentTypeName: \"DateInput\",\n componentType: DateInput,\n fieldName: \"Effective Date\",\n optionValue: defaultDateOptions,\n informationText:\n \"The effective date is the date your submission should be added or removed from the dm+d database. The change will be seen on the dm+d browser up to 5 working days later.\",\n validationRules: [hasToBeFilledDate, hasToBeTodayOrFuture],\n },\n\n /**\n * INGREDIENTS\n */\n\n seperatorIngredients: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Ingredients\",\n },\n descriptionIngredients: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"For each active ingredient, please complete all the appropriate fields below. Then click 'Save Ingredient' to add the ingredient to your product.\",\n },\n descriptionNewIngredients: {\n componentTypeName: \"DescriptionLink\",\n componentType: DescriptionLink,\n // placeHolder:\"\",\n },\n ingredients: {\n componentTypeName: \"Ingredients\",\n componentType: Ingredients,\n fieldName: \"Ingredients\",\n validationRules: [thereMustBeAnIngAtLeast],\n removeButtonForIngsAndPacks: (obj, additionalData) => {\n let data = obj.data;\n let current = _.omit(data.ingredients.currentValue, additionalData.keyName);\n\n if (Object.keys(current).length > 0) {\n obj.setData({\n ...data,\n ingredients: {\n ...data.ingredients,\n currentValue: current,\n errorText: obj.findError(\"ingredients\", current),\n },\n });\n } else {\n obj.setData({\n ...data,\n ingredients: {\n ...data.ingredients,\n currentValue: current,\n errorText: obj.findError(\"ingredients\", current),\n },\n saveIngredientButton: {\n ...data.saveIngredientButton,\n hideFromUI: false,\n },\n addIngredientButton: {\n ...data.addIngredientButton,\n hideFromUI: true,\n },\n bops: {\n ...data.bops,\n hideFromUI: false,\n },\n ingredientName: {\n ...data.ingredientName,\n hideFromUI: false,\n },\n boss: {\n ...data.boss,\n hideFromUI: false,\n },\n numerator: {\n ...data.numerator,\n hideFromUI: false,\n },\n denominator: {\n ...data.denominator,\n hideFromUI: false,\n },\n });\n }\n },\n },\n ingredientName: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Ingredient Name\",\n placeHolder: \"Type to search ingredients\",\n apiNameForOptionValue: \"callIngredients\",\n handleData: handleDataForIngredientApi,\n validationRules: [hasToBeFilled],\n informationText: \"Ingredient means the active ingredient(s) as listed on the SPC.\",\n dontSendToApi: true,\n hideFromUI: false,\n },\n bops: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Basis of Pharmaceutical Strength (BOPS)\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: bops,\n informationText:\n \"The BOPS indicates which part of the active ingredient the strength is based on – whether the strength is based on the whole active ingredient (based on ingredient substance) or whether the strength is based on just the base part of the active ingredient (based on base substance). See the glossary in the Help section for more guidance.\",\n dontSendToApi: true,\n hideFromUI: false,\n onChange: (obj, bopsCurrentValue) => {\n let data = obj.data;\n\n data.boss = {\n ...data.boss,\n currentValue: null,\n // set BoSS to optional field if the selected value is “Based on Ingredient Substance”\n validationRules: bopsCurrentValue.value === \"0001\" ? null : [hasToBeFilled],\n };\n data.boss.errorText = obj.findError(\"boss\", null);\n }\n },\n boss: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Basis of Substance Strength (BoSS)\",\n placeHolder: \"Type to search ingredients\",\n apiNameForOptionValue: \"callIngredients\",\n handleData: handleDataForIngredientApi,\n validationRules: [hasToBeFilled],\n informationText:\n \"If you have selected ‘based on base substance’ in the box above, click on the \\\"Lookup\\\" button, type in all or part of the BOSS and click \\\"Search\\\". Select the exact BOSS match.\",\n dontSendToApi: true,\n hideFromUI: false,\n },\n numerator: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Numerator Strength\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnit,acceptDecimal],\n optionValue: units,\n informationText:\n \"Type the product strength in the first box e.g. 50. Select the units of strength in the second box e.g. mg \",\n dontSendToApi: true,\n hideFromUI: false,\n },\n denominator: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Denominator Strength\",\n placeHolder: \"Type and Select\",\n validationRules: [UnitValueValidation,acceptDecimal], \n optionValue: units,\n informationText:\n \"For 'per unit strength' products (50mg per 1 ml) type the denominator strength in the first box e.g. ‘1’ and select units of strength in the second box e.g. ‘ml’. Please enter products per unit measure (per 1 gram or per 1ml). See the Help section glossary for more guidance.\",\n dontSendToApi: true,\n hideFromUI: false,\n errorText: []\n },\n saveIngredientButton: {\n componentTypeName: \"Button\",\n componentType: Button,\n hideFromUI: false,\n dontSendToApi: true,\n text: \"SAVE INGREDIENT\",\n buttonType: \"deactive\", // \"deactive\" || \"\"\n onClick: (obj, additionalData) => {\n let data = obj.data;\n if (\n data?.ingredientName?.errorText?.length !== 0 ||\n data?.bops?.errorText?.length !== 0 ||\n data?.boss?.errorText?.length !== 0 ||\n data?.numerator?.errorText?.length !== 0 ||\n data?.denominator?.errorText?.length !== 0\n ) {\n return;\n }\n\n let newIngredientsCurrentValue = {\n ...data.ingredients.currentValue,\n [data.ingredientName.currentValue.value]: {\n ingredientName: { currentValue: data.ingredientName.currentValue },\n bops: { currentValue: data.bops.currentValue },\n boss: { currentValue: data.boss.currentValue },\n numerator: { currentValue: data.numerator.currentValue },\n denominator: { currentValue: data.denominator.currentValue },\n },\n };\n\n if (!obj.settings.isThereAnyChange) {\n obj.setSettings({ ...obj.settings, isThereAnyChange: true });\n }\n\n obj.setData({\n ...data,\n ingredients: {\n ...data.ingredients,\n currentValue: newIngredientsCurrentValue,\n errorText: obj.findError(\"ingredients\", newIngredientsCurrentValue),\n },\n saveIngredientButton: {\n ...data.saveIngredientButton,\n hideFromUI: true,\n },\n addIngredientButton: {\n ...data.addIngredientButton,\n hideFromUI: false,\n },\n bops: {\n ...data.bops,\n currentValue: null,\n errorText: obj.findError(\"bops\", null),\n hideFromUI: true,\n },\n ingredientName: {\n ...data.ingredientName,\n currentValue: null,\n errorText: obj.findError(\"ingredientName\", null),\n hideFromUI: true,\n },\n boss: {\n ...data.boss,\n currentValue: null,\n errorText: obj.findError(\"boss\", null),\n hideFromUI: true,\n },\n numerator: {\n ...data.numerator,\n currentValue: null,\n errorText: obj.findError(\"numerator\", null),\n hideFromUI: true,\n },\n denominator: {\n ...data.denominator,\n currentValue: null ,\n errorText: obj.findError(\"denominator\", null),\n hideFromUI: true,\n },\n });\n },\n },\n addIngredientButton: {\n componentTypeName: \"Button\",\n componentType: Button,\n hideFromUI: true,\n dontSendToApi: true,\n text: \"NEW INGREDIENT\",\n onClick: (obj, additionalData) => {\n let data = obj.data;\n\n obj.setData({\n ...data,\n saveIngredientButton: {\n ...data.saveIngredientButton,\n hideFromUI: false,\n },\n addIngredientButton: {\n ...data.addIngredientButton,\n hideFromUI: true,\n },\n bops: {\n ...data.bops,\n hideFromUI: false,\n },\n ingredientName: {\n ...data.ingredientName,\n hideFromUI: false,\n },\n boss: {\n ...data.boss,\n hideFromUI: false,\n },\n numerator: {\n ...data.numerator,\n hideFromUI: false,\n },\n denominator: {\n ...data.denominator,\n hideFromUI: false,\n },\n });\n },\n },\n\n /**\n * EXCIPIENTS\n */\n\n seperatorExcipients: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Excipients\",\n },\n productFlavour: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Product Flavour\",\n placeHolder: \"Type and Select\",\n optionValue: flavours,\n informationText: \"If applicable, select any product flavourings from the list.\",\n },\n glutenFreeProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Gluten Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText: \"Is your product gluten free?\",\n validationRules: [hasToBeFilled],\n },\n preservativeFreeProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Preservative Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If your product is an eye drop, is it free from preservatives? If your product is not an eye drop, select not applicable.\",\n validationRules: [hasToBeFilled],\n },\n sugarFreeProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Sugar Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If this product has prolonged contact in the mouth, is it sugar free? See glossary for examples.\",\n validationRules: [hasToBeFilled],\n },\n cfcFreeProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"CFC Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If this product has prolonged contact in the mouth, is it sugar free? See glossary for examples.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * LICENCE DETAILS\n */\n\n seperatorLicense: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Licence Details\",\n },\n controlledDrugCategory: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Controlled Drug Category\",\n placeHolder: \"Type and Select\",\n optionValue: controlledDrugCategories,\n informationText:\n \"Some drugs have restrictions on them according to the Misuse of Drugs Act 1971 and Misuse of Drugs Regulations. The SPC will state any restrictions and the controlled drug schedule.\",\n validationRules: [hasToBeFilled],\n },\n currentLicensingAuthority: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Current Licensing Authority\",\n placeHolder: \"Type and Select\",\n optionValue: currentLicensing,\n informationText: \"For medicines registered by the MHRA or EMA, select Medicines-MHRA.\",\n validationRules: [hasToBeFilled],\n },\n restrictionsOnAvailability: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Restrictions on Availability\",\n placeHolder: \"Type and Select\",\n optionValue: restrictions,\n informationText:\n \"Some products have restrictions on them e.g. only available through hospitals or through specific doctors or pharmacists. The glossary gives full definitions.\",\n validationRules: [hasToBeFilled],\n },\n emaAdditionalMonitoring: {\n componentTypeName: \"RadioButton\",\n componentType: RadioButton,\n fieldName: \"EMA Additional Monitoring\",\n placeHolder: \"Type and Select\",\n optionValue: yesNo,\n informationText:\n \"This medicinal product is subject to additional monitoring. Its documentation carries a black triangle.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * PACK DETAILS\n */\n\n seperatorPacks: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Pack Details\",\n },\n descriptionPacks: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Use the fields below to specify each pack. Click ‘Save Pack’ to add more packs to the products.\",\n },\n packs: {\n isASection: true,\n componentTypeName: \"Packs\",\n componentType: Packs,\n fieldName: \"Packs\",\n validationRules: [thereMustBeAnPackAtLeast],\n removeButtonForIngsAndPacks: (obj, additionalData) => {\n let data = obj.data;\n let current = _.omit(data.packs.currentValue, additionalData.keyName);\n\n if (Object.keys(current).length > 0) {\n obj.setData({\n ...data,\n packs: {\n ...data.packs,\n currentValue: current,\n errorText: obj.findError(\"packs\", current),\n },\n });\n } else {\n obj.setData({\n ...data,\n packs: {\n ...data.packs,\n currentValue: current,\n errorText: obj.findError(\"packs\", current),\n },\n savePackButton: {\n ...data.savePackButton,\n hideFromUI: false,\n },\n addPackButton: {\n ...data.addPackButton,\n hideFromUI: true,\n },\n\n quantity: {\n ...data.quantity,\n hideFromUI: false,\n },\n legalCategory: {\n ...data.legalCategory,\n hideFromUI: false,\n },\n subpackInfo: {\n ...data.subpackInfo,\n hideFromUI: false,\n },\n gtinCodes: {\n ...data.gtinCodes,\n hideFromUI: false,\n },\n calendarPack: {\n ...data.calendarPack,\n hideFromUI: false,\n },\n hospitalPack: {\n ...data.hospitalPack,\n hideFromUI: false,\n },\n limitedStability: {\n ...data.limitedStability,\n hideFromUI: false,\n },\n price: {\n ...data.price,\n hideFromUI: false,\n },\n });\n }\n },\n },\n quantity: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Quantity\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnit,shouldBeNumber],\n informationText:\n \"Quantity/Unit means the number and type of ‘units’ in the pack e.g. 28 tablets or 5 cartridges. Type a numerical quantity in the first box. Select the units from the drop down list. E.g. ‘28’ and ‘tablet’ or ‘10’ and ‘ml’ or ‘5’ and ‘cartridge.\",\n optionValue: units,\n dontSendToApi: true,\n hideFromUI: false,\n },\n legalCategory: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Legal Category\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: legalCategories,\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"The legal category of this specific product pack. Use ‘not applicable’ for all non-medicine packs e.g. appliances.\",\n },\n subpackInfo: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Subpack Information\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled],\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"Subpack information explains how the product is packaged e.g. the number of strips of tablets in a pack. For a 28 tablet pack in 2 strips, enter 2x14. For a 28 tablet pack in 1 strip enter 1x28. For a single pack of 1 vial, 1 ampoule, 1 tube or 1 tub, enter not applicable.\",\n },\n gtinCodes: {\n componentTypeName: \"MultipleTextInput\",\n componentType: MultipleTextInput,\n fieldName: \"GTIN Codes\",\n placeHolder: \"Type and add\",\n validationRules: [hasToBeFilled, gtinCodesControl],\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"This is a mandatory field - include between 1 and 20 GTIN codes. Use the drop down list to increase the number of GTIN fields.\",\n },\n calendarPack: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Calendar Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"A calendar pack is a blister or strip pack showing the days of the week or month against each of the several units in the pack.\",\n },\n hospitalPack: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Hospital Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"A hospital pack is one that is only made available through hospital prescribing.\",\n },\n limitedStability: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Limited Stability\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"Some preparations require a diluent/vehicle to be added to them. If the resultant liquid preparation has a stability of 13 days or less, mark the preparation as limited stability.\",\n },\n price: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Price (in pence)\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled, hasToBePrice],\n dontSendToApi: true,\n hideFromUI: false,\n informationText:\n \"The price of this specific product pack in pence, sterling. For example enter £3.62 as 362. For new product packs that are awaiting price confirmation, please enter 1. See ‘entering data’ section of the user manual for more details.\",\n },\n savePackButton: {\n componentTypeName: \"Button\",\n componentType: Button,\n hideFromUI: false,\n dontSendToApi: true,\n text: \"SAVE PACK\",\n buttonType: \"deactive\", // \"deactive\" || \"\"\n onClick: (obj, additionalData) => {\n let data = obj.data;\n\n const NAMES_TO_CHECK = [\n \"quantity\",\n \"legalCategory\",\n \"subpackInfo\",\n \"gtinCodes\",\n \"calendarPack\",\n \"hospitalPack\",\n \"limitedStability\",\n \"price\",\n ];\n\n const totalErrors = NAMES_TO_CHECK.reduce((total, name) => {\n const field = data[name];\n const errorText = field?.errorText;\n const count = errorText?.length || 0;\n\n return total + count;\n }, 0);\n\n if (totalErrors > 0) {\n return;\n }\n\n let newPacksCurrentValue = {\n ...data.packs.currentValue,\n [data.quantity.currentValue.input + \"\" + data.quantity.currentValue.unit.label]: {\n quantity: { currentValue: data.quantity.currentValue },\n legalCategory: { currentValue: data.legalCategory.currentValue },\n subpackInfo: { currentValue: data.subpackInfo.currentValue },\n gtinCodes: { currentValue: data.gtinCodes.currentValue },\n calendarPack: { currentValue: data.calendarPack.currentValue },\n hospitalPack: { currentValue: data.hospitalPack.currentValue },\n limitedStability: { currentValue: data.limitedStability.currentValue },\n price: { currentValue: data.price.currentValue },\n },\n };\n\n if (!obj.settings.isThereAnyChange) {\n obj.setSettings({ ...obj.settings, isThereAnyChange: true });\n }\n\n obj.setData({\n ...data,\n packs: {\n ...data.packs,\n currentValue: newPacksCurrentValue,\n errorText: obj.findError(\"packs\", newPacksCurrentValue),\n },\n savePackButton: {\n ...data.savePackButton,\n hideFromUI: true,\n },\n addPackButton: {\n ...data.addPackButton,\n hideFromUI: false,\n },\n\n quantity: {\n ...data.quantity,\n currentValue: null,\n errorText: obj.findError(\"quantity\", null),\n hideFromUI: true,\n },\n legalCategory: {\n ...data.legalCategory,\n currentValue: null,\n errorText: obj.findError(\"legalCategory\", null),\n hideFromUI: true,\n },\n subpackInfo: {\n ...data.subpackInfo,\n currentValue: null,\n errorText: obj.findError(\"subpackInfo\", null),\n hideFromUI: true,\n },\n gtinCodes: {\n ...data.gtinCodes,\n currentValue: null,\n errorText: obj.findError(\"gtinCodes\", null),\n hideFromUI: true,\n },\n calendarPack: {\n ...data.calendarPack,\n currentValue: null,\n errorText: obj.findError(\"calendarPack\", null),\n hideFromUI: true,\n },\n hospitalPack: {\n ...data.hospitalPack,\n currentValue: null,\n errorText: obj.findError(\"hospitalPack\", null),\n hideFromUI: true,\n },\n limitedStability: {\n ...data.limitedStability,\n currentValue: null,\n errorText: obj.findError(\"limitedStability\", null),\n hideFromUI: true,\n },\n price: {\n ...data.price,\n currentValue: null,\n errorText: obj.findError(\"price\", null),\n hideFromUI: true,\n },\n });\n },\n },\n addPackButton: {\n componentTypeName: \"Button\",\n componentType: Button,\n hideFromUI: true,\n dontSendToApi: true,\n text: \"NEW PACK\",\n onClick: (obj, additionalData) => {\n let data = obj.data;\n\n obj.setData({\n ...data,\n savePackButton: {\n ...data.savePackButton,\n hideFromUI: false,\n },\n addPackButton: {\n ...data.addPackButton,\n hideFromUI: true,\n },\n\n quantity: {\n ...data.quantity,\n hideFromUI: false,\n },\n legalCategory: {\n ...data.legalCategory,\n hideFromUI: false,\n },\n subpackInfo: {\n ...data.subpackInfo,\n hideFromUI: false,\n },\n gtinCodes: {\n ...data.gtinCodes,\n hideFromUI: false,\n },\n calendarPack: {\n ...data.calendarPack,\n hideFromUI: false,\n },\n hospitalPack: {\n ...data.hospitalPack,\n hideFromUI: false,\n },\n limitedStability: {\n ...data.limitedStability,\n hideFromUI: false,\n },\n price: {\n ...data.price,\n hideFromUI: false,\n },\n });\n },\n },\n\n /**\n * SMPC FILE\n */\n\n seperatorSMPC: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"SmPC File\",\n },\n smpcFile: {\n componentTypeName: \"FileInput\",\n componentType: FileInput,\n fieldName: \"SmPC File\",\n validationRules: [hasToBeDocOrPDF, isFileSizeWithinRange],\n },\n});\n\n//-------------------------------------------------------------\n// \t\tNEW PACK\n//-------------------------------------------------------------\n\nexport const NewPack = ({units, legalCategories}) => ({\n seperatorIntro: {\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Details\",\n },\n relatedProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Related Product\",\n placeHolder: \"Type to search a product\",\n apiNameForOptionValue: \"callAMP\",\n handleData: handleDataForIngredientApi, // in some cases, there are different object keys in API. this func checks and converts them.\n informationText:\n \"The product name is usually the trade name of the product + strength + form e.g. BrandX 50mg tablets or BrandX 150mg/15ml solution for injection ampoules.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * EFFECTIVE DATE\n */\n\n seperatorEffectiveDate: {\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Effective Date\",\n },\n descriptionEffectiveDate: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please enter the effective date for changes. If the date that you choose is within 2 weeks of the current date, please note that the NHS BSA cannot commit to have actioned your request by this date.\",\n },\n effectiveDate: {\n componentTypeName: \"DateInput\",\n componentType: DateInput,\n fieldName: \"Effective Date\",\n optionValue: defaultDateOptions,\n informationText:\n \"The effective date is the date your submission should be added or removed from the dm+d database. The change will be seen on the dm+d browser up to 5 working days later.\",\n validationRules: [hasToBeFilledDate, hasToBeTodayOrFuture],\n },\n\n /**\n * PACK DETAILS\n */\n seperatorPackDetails: {\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Pack Details\",\n },\n quantity: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Quantity\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnit,shouldBeNumber],\n informationText:\n \"Quantity/Unit means the number and type of ‘units’ in the pack e.g. 28 tablets or 5 cartridges. Type a numerical quantity in the first box. Select the units from the drop down list. E.g. ‘28’ and ‘tablet’ or ‘10’ and ‘ml’ or ‘5’ and ‘cartridge.\",\n optionValue: units,\n },\n legalCategory: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Legal Category\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: legalCategories,\n informationText:\n \"The legal category of this specific product pack. Use ‘not applicable’ for all non-medicine packs e.g. appliances.\",\n },\n subpackInfo: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Subpack Information\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled],\n informationText:\n \"Subpack information explains how the product is packaged e.g. the number of strips of tablets in a pack. For a 28 tablet pack in 2 strips, enter 2x14. For a 28 tablet pack in 1 strip enter 1x28. For a single pack of 1 vial, 1 ampoule, 1 tube or 1 tub, enter not applicable.\",\n },\n gtinCodes: {\n componentTypeName: \"MultipleTextInput\",\n componentType: MultipleTextInput,\n fieldName: \"GTIN Codes\",\n placeHolder: \"Type and add\",\n validationRules: [hasToBeFilled, gtinCodesControl],\n informationText:\n \"This is a mandatory field - include between 1 and 20 GTIN codes. Use the drop down list to increase the number of GTIN fields.\",\n },\n calendarPack: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Calendar Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"A calendar pack is a blister or strip pack showing the days of the week or month against each of the several units in the pack.\",\n },\n hospitalPack: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Hospital Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"A hospital pack is one that is only made available through hospital prescribing.\",\n },\n limitedStability: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Limited Stability\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"Some preparations require a diluent/vehicle to be added to them. If the resultant liquid preparation has a stability of 13 days or less, mark the preparation as limited stability.\",\n },\n price: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Price (in pence)\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled, hasToBePrice],\n informationText:\n \"The price of this specific product pack in pence, sterling. For example enter £3.62 as 362. For new product packs that are awaiting price confirmation, please enter 1. See ‘entering data’ section of the user manual for more details.\",\n },\n\n /**\n * SMPC FILE\n */\n\n seperatorSMPC: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"SmPC File\",\n },\n\n smpcFile: {\n componentTypeName: \"FileInput\",\n componentType: FileInput,\n fieldName: \"SmPC File\",\n validationRules: [hasToBeDocOrPDF, isFileSizeWithinRange],\n },\n});\n\n//-------------------------------------------------------------\n// \t\tUPDATE PACK\n//-------------------------------------------------------------\n\nexport const UpdatePack = ({units, legalCategories}) => ({\n relatedProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Select a Product\",\n placeHolder: \"Type to search a product\",\n apiNameForOptionValue: \"callAMP\",\n handleData: handleDataForIngredientApi,\n informationText:\n \"The product name is usually the trade name of the product + strength + form e.g. BrandX 50mg tablets or BrandX 150mg/15ml solution for injection ampoules.\",\n validationRules: [hasToBeFilled],\n },\n\n relatedPack: {\n internalState: 2,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Select a Pack\",\n placeHolder: \"Type to search a pack\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * EFFECTIVE DATE\n */\n\n seperatorEffectiveDate: {\n internalState: 3,\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Effective Date\",\n },\n descriptionEffectiveDate: {\n internalState: 3,\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please enter the effective date for changes. If the date that you choose is within 2 weeks of the current date, please note that the NHS BSA cannot commit to have actioned your request by this date.\",\n },\n effectiveDate: {\n internalState: 3,\n componentTypeName: \"DateInput\",\n componentType: DateInput,\n fieldName: \"Effective Date\",\n optionValue: defaultDateOptions,\n informationText:\n \"The effective date is the date your submission should be added or removed from the dm+d database. The change will be seen on the dm+d browser up to 5 working days later.\",\n validationRules: [hasToBeFilledDate, hasToBeTodayOrFuture],\n },\n discontinuePack: {\n internalState: 3,\n componentTypeName: \"RadioButton\",\n componentType: RadioButton,\n fieldName: \"Discontinue Pack?\",\n placeHolder: \"Type and Select\",\n optionValue: yesNo,\n validationRules: [hasToBeFilled],\n },\n discontinueReason: {\n internalState: 3,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Discontinue Reason\",\n placeHolder: \"Reason for discontinuation\",\n informationText: \"\",\n validationRules: [hasToBeFilledIfVisible],\n hideFromUI: true,\n },\n discontinueQuantity: {\n internalState: 3,\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Quantity\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnitIfVisible,shouldBeNumber],\n informationText:\n \"Quantity/Unit means the number and type of ‘units’ in the pack e.g. 28 tablets or 5 cartridges. Type a numerical quantity in the first box. Select the units from the drop down list. E.g. ‘28’ and ‘tablet’ or ‘10’ and ‘ml’ or ‘5’ and ‘cartridge.\",\n optionValue: units,\n },\n\n singlePriceUpdate: {\n internalState: 4,\n componentTypeName: \"RadioButton\",\n componentType: RadioButton,\n fieldName: \"Hide me from UI. I'm just for API.\",\n placeHolder: \"\",\n currentValue: {\n label: \"False\",\n value: false,\n },\n inComingSavedValue: {},\n changedFieldNames: [],\n optionValue: trueFalse,\n hideFromUI: true,\n },\n\n /**\n * PACK DETAILS\n */\n seperatorPackDetails: {\n internalState: 4,\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Pack Details\",\n },\n quantity: {\n internalState: 4,\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Quantity\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnit,shouldBeNumber],\n informationText:\n \"Quantity/Unit means the number and type of ‘units’ in the pack e.g. 28 tablets or 5 cartridges. Type a numerical quantity in the first box. Select the units from the drop down list. E.g. ‘28’ and ‘tablet’ or ‘10’ and ‘ml’ or ‘5’ and ‘cartridge.\",\n optionValue: units,\n },\n legalCategory: {\n internalState: 4,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Legal Category\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: legalCategories,\n informationText:\n \"The legal category of this specific product pack. Use ‘not applicable’ for all non-medicine packs e.g. appliances.\",\n },\n subpackInfo: {\n internalState: 4,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Subpack Information\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled],\n informationText:\n \"Subpack information explains how the product is packaged e.g. the number of strips of tablets in a pack. For a 28 tablet pack in 2 strips, enter 2x14. For a 28 tablet pack in 1 strip enter 1x28. For a single pack of 1 vial, 1 ampoule, 1 tube or 1 tub, enter not applicable.\",\n },\n gtinCodes: {\n internalState: 4,\n componentTypeName: \"MultipleTextInput\",\n componentType: MultipleTextInput,\n fieldName: \"GTIN Codes\",\n placeHolder: \"Type and add\",\n validationRules: [hasToBeFilled, gtinCodesControl],\n informationText:\n \"This is a mandatory field - include between 1 and 20 GTIN codes. Use the drop down list to increase the number of GTIN fields.\",\n },\n calendarPack: {\n internalState: 4,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Calendar Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"A calendar pack is a blister or strip pack showing the days of the week or month against each of the several units in the pack.\",\n },\n hospitalPack: {\n internalState: 4,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Hospital Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"A hospital pack is one that is only made available through hospital prescribing.\",\n },\n limitedStability: {\n internalState: 4,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Limited Stability\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n informationText:\n \"Some preparations require a diluent/vehicle to be added to them. If the resultant liquid preparation has a stability of 13 days or less, mark the preparation as limited stability.\",\n },\n price: {\n internalState: 4,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Price (in pence)\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled, hasToBePrice],\n informationText:\n \"The price of this specific product pack in pence, sterling. For example enter £3.62 as 362. For new product packs that are awaiting price confirmation, please enter 1. See ‘entering data’ section of the user manual for more details.\",\n },\n\n /**\n * SMPC FILE\n */\n\n seperatorSMPC: {\n isASection: true,\n internalState: 4,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"SmPC File\",\n },\n\n smpcFile: {\n componentTypeName: \"FileInput\",\n componentType: FileInput,\n internalState: 4,\n fieldName: \"SmPC File\",\n validationRules: [hasToBeDocOrPDF, isFileSizeWithinRange],\n },\n});\n\n//-------------------------------------------------------------\n// \t\tUPDATE PRODUCT\n//-------------------------------------------------------------\n\nexport const UpdateProduct = ({formulations, licensedRoutes, flavours, controlledDrugCategories, currentLicensing, restrictions, bops, units, legalCategories}) => ({\n relatedProduct: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Existing Product Name\",\n placeHolder: \"Type to search a product\",\n apiNameForOptionValue: \"callAMP\",\n handleData: handleDataForIngredientApi,\n informationText:\n \"The product name is usually the trade name of the product + strength + form e.g. BrandX 50mg tablets or BrandX 150mg/15ml solution for injection ampoules.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * EFFECTIVE DATE\n */\n\n seperatorEffectiveDate: {\n internalState: 2,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Effective Date\",\n },\n descriptionEffectiveDate: {\n internalState: 2,\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please enter the effective date for changes. If the date that you choose is within 2 weeks of the current date, please note that the NHS BSA cannot commit to have actioned your request by this date.\",\n },\n effectiveDate: {\n internalState: 2,\n componentTypeName: \"DateInput\",\n componentType: DateInput,\n fieldName: \"Effective Date\",\n informationText:\n \"The effective date is the date your submission should be added or removed from the dm+d database. The change will be seen on the dm+d browser up to 5 working days later.\",\n optionValue: defaultDateOptions,\n validationRules: [hasToBeFilledDate, hasToBeTodayOrFuture],\n },\n discontinueProduct: {\n internalState: 2,\n componentTypeName: \"RadioButton\",\n componentType: RadioButton,\n fieldName: \"Discontinue Product?\",\n placeHolder: \"Type and Select\",\n optionValue: yesNo,\n validationRules: [hasToBeFilled],\n // uniqueDispatch: \"updateProductSubmission_handleDiscontinueProduct\",\n },\n discontinueReason: {\n internalState: 2,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Discontinue Reason\",\n placeHolder: \"Reason for discontinuation\",\n informationText: \"\",\n validationRules: [hasToBeFilledIfVisible],\n hideFromUI: true,\n },\n\n /**\n * DETAILS\n */\n\n seperatorIntro: {\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Details\",\n internalState: 3,\n },\n name: {\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Product Name\",\n internalState: 3,\n // alwaysNonEditable: true,\n placeHolder: \"Type New Product Name\",\n informationText:\n \"Changing the product name is optional. The product name is usually the trade name of the product + strength + form e.g. BrandX 50mg tablets or BrandX 150mg/15ml solution for injection ampoules.\",\n },\n relatedVMP: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n internalState: 3,\n alwaysNonEditable: true,\n fieldName: \"Related VMP\",\n placeHolder: \"Type to search a VMP\",\n apiNameForOptionValue: \"callVMP\",\n handleData: handleDataForIngredientApi,\n informationText:\n \"Virtual Medicinal Product (VMP) name is most commonly the generic name + strength + form, e.g. paracetamol 500mg capsule.\",\n validationRules: [hasToBeFilled],\n },\n formulation: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Formulation\",\n internalState: 3,\n alwaysNonEditable: true,\n optionValue: formulations,\n },\n\n licensedRoutes: {\n internalState: 3,\n componentTypeName: \"MultipleSelectBox\",\n componentType: MultipleSelectBox,\n fieldName: \"Licensed Routes\",\n placeHolder: \"Type and Select\",\n optionValue: licensedRoutes,\n informationText: \"Select only the licensed route of administration as listed on the SPC.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * INGREDIENTS\n */\n\n seperatorIngredients: {\n isASection: true,\n internalState: 3,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Ingredients\",\n },\n ingredients: {\n internalState: 3,\n alwaysNonEditable: true,\n componentTypeName: \"Ingredients\",\n componentType: Ingredients,\n fieldName: \"Ingredients\",\n validationRules: [thereMustBeAnIngAtLeast],\n removeButtonForIngsAndPacks: () => {},\n },\n ingredientName: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n internalState: 3,\n apiNameForOptionValue: \"callIngredients\",\n handleData: handleDataForIngredientApi,\n validationRules: [hasToBeFilled],\n dontSendToApi: true,\n hideFromUI: true,\n },\n bops: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n internalState: 3,\n validationRules: [hasToBeFilled],\n optionValue: bops,\n dontSendToApi: true,\n hideFromUI: true,\n },\n boss: {\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n internalState: 3,\n apiNameForOptionValue: \"callIngredients\",\n handleData: handleDataForIngredientApi,\n validationRules: [hasToBeFilled],\n dontSendToApi: true,\n hideFromUI: true,\n },\n numerator: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n internalState: 3,\n validationRules: [hasToBeFilledUnit],\n optionValue: units,\n dontSendToApi: true,\n hideFromUI: true,\n },\n denominator: {\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n internalState: 3,\n validationRules: [hasToBeFilledUnit],\n optionValue: units,\n dontSendToApi: true,\n hideFromUI: true,\n },\n\n /**\n * EXCIPIENTS\n */\n\n seperatorExcipients: {\n internalState: 3,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Excipients\",\n },\n productFlavour: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Product Flavour\",\n placeHolder: \"Type and Select\",\n optionValue: flavours,\n informationText: \"If applicable, select any product flavourings from the list.\",\n },\n glutenFreeProduct: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Gluten Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText: \"Is your product gluten free?\",\n validationRules: [hasToBeFilled],\n },\n preservativeFreeProduct: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Preservative Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If your product is an eye drop, is it free from preservatives? If your product is not an eye drop, select not applicable.\",\n validationRules: [hasToBeFilled],\n },\n sugarFreeProduct: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Sugar Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If this product has prolonged contact in the mouth, is it sugar free? See glossary for examples.\",\n validationRules: [hasToBeFilled],\n },\n cfcFreeProduct: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"CFC Free Product\",\n placeHolder: \"Type and Select\",\n optionValue: yesNoNA,\n informationText:\n \"If this product has prolonged contact in the mouth, is it sugar free? See glossary for examples.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * LICENCE DETAILS\n */\n\n seperatorLicense: {\n internalState: 3,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Licence Details\",\n },\n controlledDrugCategory: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Controlled Drug Category\",\n placeHolder: \"Type and Select\",\n optionValue: controlledDrugCategories,\n informationText:\n \"Some drugs have restrictions on them according to the Misuse of Drugs Act 1971 and Misuse of Drugs Regulations. The SPC will state any restrictions and the controlled drug schedule.\",\n validationRules: [hasToBeFilled],\n },\n currentLicensingAuthority: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Current Licensing Authority\",\n placeHolder: \"Type and Select\",\n optionValue: currentLicensing,\n informationText: \"For medicines registered by the MHRA or EMA, select Medicines-MHRA.\",\n validationRules: [hasToBeFilled],\n },\n restrictionsOnAvailability: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Restrictions on Availability\",\n placeHolder: \"Type and Select\",\n optionValue: restrictions,\n informationText:\n \"Some products have restrictions on them e.g. only available through hospitals or through specific doctors or pharmacists. The glossary gives full definitions.\",\n validationRules: [hasToBeFilled],\n },\n emaAdditionalMonitoring: {\n internalState: 3,\n componentTypeName: \"RadioButton\",\n componentType: RadioButton,\n fieldName: \"EMA Additional Monitoring\",\n placeHolder: \"Type and Select\",\n optionValue: yesNo,\n informationText:\n \"This medicinal product is subject to additional monitoring. Its documentation carries a black triangle.\",\n validationRules: [hasToBeFilled],\n },\n\n /**\n * PACK DETAILS\n */\n\n seperatorPacks: {\n internalState: 3,\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Pack Details\",\n },\n descriptionPacks: {\n internalState: 3,\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"\",\n },\n packs: {\n internalState: 3,\n componentTypeName: \"Packs\",\n componentType: Packs,\n fieldName: \"Packs\",\n validationRules: [thereMustBeAnPackAtLeast],\n },\n quantity: {\n internalState: 3,\n componentTypeName: \"UnitSelect\",\n componentType: UnitSelect,\n fieldName: \"Quantity\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilledUnit],\n informationText:\n \"Quantity/Unit means the number and type of ‘units’ in the pack e.g. 28 tablets or 5 cartridges. Type a numerical quantity in the first box. Select the units from the drop down list. E.g. ‘28’ and ‘tablet’ or ‘10’ and ‘ml’ or ‘5’ and ‘cartridge.\",\n optionValue: units,\n dontSendToApi: true,\n hideFromUI: true,\n },\n legalCategory: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Legal Category\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: legalCategories,\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"The legal category of this specific product pack. Use ‘not applicable’ for all non-medicine packs e.g. appliances.\",\n },\n subpackInfo: {\n internalState: 3,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Subpack Information\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled],\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"Subpack information explains how the product is packaged e.g. the number of strips of tablets in a pack. For a 28 tablet pack in 2 strips, enter 2x14. For a 28 tablet pack in 1 strip enter 1x28. For a single pack of 1 vial, 1 ampoule, 1 tube or 1 tub, enter not applicable.\",\n },\n gtinCodes: {\n internalState: 3,\n componentTypeName: \"MultipleTextInput\",\n componentType: MultipleTextInput,\n fieldName: \"GTIN Codes\",\n placeHolder: \"Type and add\",\n validationRules: [hasToBeFilled, gtinCodesControl],\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"This is a mandatory field - include between 1 and 20 GTIN codes. Use the drop down list to increase the number of GTIN fields.\",\n },\n calendarPack: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Calendar Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"A calendar pack is a blister or strip pack showing the days of the week or month against each of the several units in the pack.\",\n },\n hospitalPack: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Hospital Pack\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"A hospital pack is one that is only made available through hospital prescribing.\",\n },\n limitedStability: {\n internalState: 3,\n componentTypeName: \"SelectBox\",\n componentType: SelectBox,\n fieldName: \"Limited Stability\",\n placeHolder: \"Type and Select\",\n validationRules: [hasToBeFilled],\n optionValue: yesNo,\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"Some preparations require a diluent/vehicle to be added to them. If the resultant liquid preparation has a stability of 13 days or less, mark the preparation as limited stability.\",\n },\n price: {\n internalState: 3,\n componentTypeName: \"TextInput\",\n componentType: TextInput,\n fieldName: \"Price (in pence)\",\n placeHolder: \"Type\",\n validationRules: [hasToBeFilled, hasToBePrice],\n dontSendToApi: true,\n hideFromUI: true,\n informationText:\n \"The price of this specific product pack in pence, sterling. For example enter £3.62 as 362. For new product packs that are awaiting price confirmation, please enter 1. See ‘entering data’ section of the user manual for more details.\",\n },\n savePackButton: {\n internalState: 3,\n componentTypeName: \"Button\",\n componentType: Button,\n hideFromUI: true,\n dontSendToApi: true,\n text: \"SAVE PACK\",\n buttonType: \"deactive\", // \"deactive\" || \"\"\n onClick: (obj, additionalData) => {\n let data = obj.data;\n\n const NAMES_TO_CHECK = [\n \"quantity\",\n \"legalCategory\",\n \"subpackInfo\",\n \"gtinCodes\",\n \"calendarPack\",\n \"hospitalPack\",\n \"limitedStability\",\n \"price\",\n ];\n\n const totalErrors = NAMES_TO_CHECK.reduce((total, name) => {\n const field = data[name];\n const errorText = field?.errorText;\n const count = errorText?.length || 0;\n\n return total + count;\n }, 0);\n\n if (totalErrors > 0) {\n return;\n }\n\n let newPacksCurrentValue = {\n ...data.packs.currentValue,\n [data.quantity.currentValue.input + \"\" + data.quantity.currentValue.unit.label]: {\n quantity: { currentValue: data.quantity.currentValue },\n legalCategory: { currentValue: data.legalCategory.currentValue },\n subpackInfo: { currentValue: data.subpackInfo.currentValue },\n gtinCodes: { currentValue: data.gtinCodes.currentValue },\n calendarPack: { currentValue: data.calendarPack.currentValue },\n hospitalPack: { currentValue: data.hospitalPack.currentValue },\n limitedStability: { currentValue: data.limitedStability.currentValue },\n price: { currentValue: data.price.currentValue },\n },\n };\n\n if (!obj.settings.isThereAnyChange) {\n obj.setSettings({ ...obj.settings, isThereAnyChange: true });\n }\n\n obj.setData({\n ...data,\n packs: {\n ...data.packs,\n currentValue: newPacksCurrentValue,\n errorText: obj.findError(\"packs\", newPacksCurrentValue),\n },\n savePackButton: {\n ...data.savePackButton,\n hideFromUI: true,\n },\n addPackButton: {\n ...data.addPackButton,\n hideFromUI: false,\n },\n\n quantity: {\n ...data.quantity,\n currentValue: null,\n errorText: obj.findError(\"quantity\", null),\n hideFromUI: true,\n },\n legalCategory: {\n ...data.legalCategory,\n currentValue: null,\n errorText: obj.findError(\"legalCategory\", null),\n hideFromUI: true,\n },\n subpackInfo: {\n ...data.subpackInfo,\n currentValue: null,\n errorText: obj.findError(\"subpackInfo\", null),\n hideFromUI: true,\n },\n gtinCodes: {\n ...data.gtinCodes,\n currentValue: null,\n errorText: obj.findError(\"gtinCodes\", null),\n hideFromUI: true,\n },\n calendarPack: {\n ...data.calendarPack,\n currentValue: null,\n errorText: obj.findError(\"calendarPack\", null),\n hideFromUI: true,\n },\n hospitalPack: {\n ...data.hospitalPack,\n currentValue: null,\n errorText: obj.findError(\"hospitalPack\", null),\n hideFromUI: true,\n },\n limitedStability: {\n ...data.limitedStability,\n currentValue: null,\n errorText: obj.findError(\"limitedStability\", null),\n hideFromUI: true,\n },\n price: {\n ...data.price,\n currentValue: null,\n errorText: obj.findError(\"price\", null),\n hideFromUI: true,\n },\n });\n },\n },\n\n /**\n * SMPC FILE\n */\n\n seperatorSMPC: {\n internalState: 3,\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"SmPC File\",\n },\n smpcFile: {\n componentTypeName: \"FileInput\",\n componentType: FileInput,\n internalState: 3,\n fieldName: \"SmPC File\",\n validationRules: [hasToBeDocOrPDF, isFileSizeWithinRange],\n },\n});\n\n//-------------------------------------------------------------\n// \t\tMASS PRICE UPDATE\n//-------------------------------------------------------------\n\nexport const MassPriceUpdate = {\n /**\n * EFFECTIVE DATE\n */\n\n seperatorEffectiveDate: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Effective Date\",\n },\n\n descriptionEffectiveDate: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please enter the effective date for changes. If the date that you choose is within 2 weeks of the current date, please note that the NHS BSA cannot commit to have actioned your request by this date.\",\n },\n \n effectiveDate: {\n componentTypeName: \"DateInput\",\n componentType: DateInput,\n fieldName: \"Effective Date\",\n optionValue: defaultDateOptions,\n informationText:\n \"The effective date is the date your submission should be added or removed from the dm+d database. The change will be seen on the dm+d browser up to 5 working days later.\",\n validationRules: [hasToBeFilledDate, hasToBeTodayOrFuture],\n },\n\n seperatorIntro: {\n isASection: true,\n componentTypeName: \"Seperator\",\n componentType: Seperator,\n placeHolder: \"Prices\",\n },\n\n descriptionPrices: {\n componentTypeName: \"Description\",\n componentType: Description,\n placeHolder:\n \"Please make up to 50 Price Changes using the form below\"\n },\n\n prices: {\n internalState: 1,\n componentTypeName: \"DataGrid\",\n componentType: DataGrid,\n fieldName: \"Prices\",\n placeHolder: \"Pack Prices\",\n validationRules: [thereMustBeAPriceUpdateAtLeast,maxofPriceInput]\n },\n};\n","import React, { useContext} from \"react\";\n\nimport { DataGridEdit } from \"./parts/DataGridEdit\";\n\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\nimport \"./DataGrid.scss\";\n\nexport const DataGrid = (props) => {\n const {\n data,\n keyName,\n // dispatch,\n // additionalStyle,\n // setIsFocused2, // date input - sub fields\n // viewMode\n updateData,\n } = props;\n const {\n // apiValue, // data to go back to server\n // placeHolder,\n // fieldName, // label name\n currentValue, // {label, value, UOM}\n // savedValue, // data from api\n // optionValue, // [{value,label}] for selections\n // apiNameForOptionValue, // call to retrieve\n // informationText, // tooltip\n // handleData, // when not value/label eg ingredients converts to this structure\n errorText,\n // validationRules,\n // alwaysNonEditable, // view mode not edit mode\n // uniqueDispatch,\n isReadOnly = false,\n } = data;\n const prices = currentValue || [];\n\n const { turnArrayToLines } = useContext(GlobalContext);\n\n if (!prices) {\n return
Loading...
;\n }\n\n const handleUpdatePrice = (id, value) => {\n const newPrices = [...prices].map((item) => {\n if (item.actualPackId === id) {\n // if it is an empty string, ensure that the value would not be converted to zero\n return { ...item, newPrice: value === \"\" ? undefined : +value };\n }\n return item;\n });\n\n updateData(keyName, newPrices);\n };\n\n const tableRows = prices.map((price) => {\n const { actualPackId: id, newPrice: value } = price;\n\n return (\n
\n
{price.actualPackId}
\n
{price.packName}
\n
{price.price}
\n
\n {isReadOnly === false ? (\n \n ) : (\n
{value}
\n )}\n
\n
\n );\n });\n return (\n
0 ? \"error\" : \"\")}>\n
\n \n
\n
AMPP ID
\n
Pack Name
\n
Current Price (Pence)
\n
New Price (Pence)
\n
\n \n {tableRows}\n
\n { errorText ? (\n
{turnArrayToLines(errorText)}
\n ) : null}\n
\n );\n};\n","import React, { useLayoutEffect, useContext, useState } from \"react\";\nimport { useHistory } from \"react-router-dom\";\nimport _ from \"lodash\";\n\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\nimport { UserContext } from \"contexts/UserStore.js\";\nimport { FetchAPI } from \"components/APIConnections/APIConnections.js\";\nimport { Seperator } from \"components/FormElements/Seperator/Seperator.js\";\nimport { SummarySection } from \"components/FormElements/SummarySection/SummarySection.js\";\nimport { FreeForm } from \"components/FormElements/FreeForm/FreeForm.js\";\n\nimport { UserActions } from \"./UserActions.js\";\nimport {\n NewProduct,\n NewPack,\n UpdateProduct,\n UpdatePack,\n MassPriceUpdate,\n} from \"./setup/formFields.js\";\n\nimport \"./HandleSub.scss\";\nimport \"components/FormElements/forms.scss\";\n\nexport let textFunc;\n\nexport const HandleSub = (props) => {\n const { match } = props;\n const { params } = match || {};\n let { ID } = params || undefined;\n let { submissionType } = params || undefined;\n let { submissionID } = params || undefined;\n const {\n isLoading,\n setPageName,\n setSidebarBehavior,\n setHeaderTitles,\n addSpaces,\n setIsLoading,\n disableIsLoadingWithDelay,\n typeIdentifier,\n showErrorLogsOnConsole,\n } = useContext(GlobalContext);\n const { userState } = useContext(UserContext);\n const history = useHistory();\n const action = history.location.pathname.split(\"/\")[1];\n const [data, setData] = useState();\n const [settings, setSettings] = useState();\n const [sections, setSections] = useState();\n\n /**\n * CONVERTERS AND SUPPORTER FUNCTIONS\n * @description convert data to currentValue or savedValue or nhsForm\n */\n const convertSavedValueToCurrentValue = (fieldOrfieldName, savedValue, newData = data) => {\n if (!fieldOrfieldName || !savedValue) {\n return null;\n }\n let field;\n if (typeof fieldOrfieldName === \"object\") {\n field = fieldOrfieldName;\n } else {\n field = newData[fieldOrfieldName];\n }\n let componentType = field.componentTypeName;\n let currentValue;\n let newData2 = newData;\n\n switch (componentType) {\n case \"DataGrid\":\n currentValue = savedValue;\n break;\n case \"TextInput\":\n currentValue = savedValue;\n break;\n case \"SelectBox\":\n if (field.apiNameForOptionValue) {\n currentValue = savedValue;\n } else {\n let newObj;\n for (let i in field.optionValue) {\n if (field.optionValue[i].value === savedValue) {\n newObj = field.optionValue[i];\n break;\n }\n }\n\n currentValue = {\n value: savedValue,\n label: newObj?.label,\n };\n }\n break;\n case \"MultipleSelectBox\":\n if (field.apiNameForOptionValue) {\n if (showErrorLogsOnConsole) {\n console.log(\n \"we don't have a dynamic multipleSelectBox. Fill here if needed.\",\n );\n }\n } else {\n let newObj = [];\n for (let i in field.optionValue) {\n for (let i2 in savedValue) {\n if (field.optionValue[i].value === savedValue[i2]) {\n newObj.push({\n value: savedValue[i2],\n label: field.optionValue[i].label,\n });\n }\n }\n }\n currentValue = newObj;\n }\n break;\n case \"RadioButton\":\n let newRadio = [];\n for (let i in field.optionValue) {\n if (field.optionValue[i].value === savedValue) {\n newRadio = {\n value: savedValue,\n label: field.optionValue[i].label,\n };\n }\n }\n currentValue = newRadio;\n break;\n case \"DateInput\":\n let saved2 = savedValue.split(\"-\");\n let dayValue = saved2[2];\n let monthValue = saved2[1];\n let yearValue = saved2[0];\n currentValue = {\n day: { label: dayValue, value: dayValue },\n month: {\n label: field.optionValue.month[monthValue - 1].label,\n value: monthValue,\n },\n year: { label: yearValue, value: yearValue },\n };\n break;\n case \"UnitSelect\":\n let newObj;\n for (let i in field.optionValue) {\n if (field.optionValue[i].value === savedValue.uom) {\n newObj = field.optionValue[i];\n break;\n }\n }\n\n if (!newObj) {\n break;\n }\n\n currentValue = {\n input: savedValue.value,\n unit: {\n value: savedValue.uom,\n label: newObj.label,\n },\n };\n\n break;\n case \"MultipleTextInput\":\n currentValue = savedValue;\n break;\n case \"FileInput\":\n currentValue = {\n fileUrl: savedValue.url,\n label: savedValue.value,\n value: savedValue.value,\n };\n break;\n case \"Packs\":\n case \"Ingredients\":\n let currentPack = {};\n savedValue.forEach((item) => {\n let newPack = {};\n Object.keys(item).forEach((key) => {\n let fieldName2 = key;\n let oldSaved = item[key];\n let currentElem = convertSavedValueToCurrentValue(\n newData2[fieldName2],\n oldSaved,\n newData2,\n );\n newPack[key] = { currentValue: currentElem };\n });\n\n if (componentType === \"Packs\") {\n currentPack[\n newPack?.quantity?.currentValue?.input +\n \"\" +\n newPack?.quantity?.currentValue?.unit?.label\n ] = newPack;\n } else {\n currentPack[newPack?.ingredientName?.currentValue?.value] = newPack;\n }\n });\n currentValue = currentPack;\n\n break;\n default:\n break;\n }\n\n return currentValue;\n };\n const convertCurrentValueToSavedValue = (fieldOrfieldName, currentValue) => {\n if (!fieldOrfieldName || !currentValue) {\n return null;\n }\n let field;\n if (typeof fieldOrfieldName === \"object\") {\n field = fieldOrfieldName;\n } else {\n field = data[fieldOrfieldName];\n }\n\n let componentType = field.componentTypeName;\n let savedValue;\n\n switch (componentType) {\n case \"TextInput\":\n savedValue = currentValue;\n break;\n case \"SelectBox\":\n if (field.apiNameForOptionValue) {\n savedValue = currentValue;\n } else {\n savedValue = currentValue.value;\n }\n break;\n case \"RadioButton\":\n savedValue = currentValue.value;\n break;\n case \"MultipleSelectBox\":\n let newSaved = [];\n currentValue.forEach((item) => {\n newSaved.push(item.value);\n });\n savedValue = newSaved;\n break;\n case \"DateInput\":\n if (!currentValue.day || !currentValue.month || !currentValue.year) {\n savedValue = null;\n } else {\n // use slice() method instead of string.prototype.padStart, as we need to still support IE11...\n savedValue =\n currentValue.year.value +\n \"-\" +\n (\"0\" + currentValue.month.value).slice(-2) +\n \"-\" +\n (\"0\" + currentValue.day.value).slice(-2);\n }\n\n break;\n case \"UnitSelect\":\n if (!currentValue.input || !currentValue.unit) {\n savedValue = null;\n } else {\n savedValue = {\n value: currentValue.input,\n uom: currentValue.unit.value,\n };\n }\n break;\n case \"Packs\":\n case \"Ingredients\":\n let savedPack = [];\n Object.keys(currentValue).forEach((key) => {\n let objInner = currentValue[key];\n let innerPack = {};\n Object.keys(objInner).forEach((key2) => {\n let fieldName2 = key2;\n let elem2 = objInner[key2].currentValue;\n let savedElem = convertCurrentValueToSavedValue(fieldName2, elem2);\n innerPack[key2] = savedElem;\n });\n savedPack.push(innerPack);\n });\n savedValue = savedPack;\n break;\n case \"MultipleTextInput\":\n savedValue = currentValue;\n break;\n case \"FileInput\":\n savedValue = {\n value: currentValue.label,\n url: currentValue.fileUrl,\n };\n\n break;\n case \"DataGrid\":\n savedValue = currentValue.filter((item) => item.newPrice !== undefined);\n\n break;\n default:\n break;\n }\n return savedValue;\n };\n const convertNhsFormToCurrentValue = (dataOrg, incomingForm) => {\n if (!dataOrg || !incomingForm) {\n return;\n }\n\n let data = _.cloneDeep(dataOrg);\n\n const yesNoNAConverter = (val) => {\n if (val === true) {\n return \"Yes\";\n } else if (val === false) {\n return \"No\";\n } else if (val === null) {\n return \"NA\";\n }\n return undefined;\n };\n\n /**\n * IsThere function\n * @description we need this function because some NHS values are coming null but it doesn't mean that it is null.\n * \"null\" is a variable in nhs form. so, I have to handle them as well.\n */\n const formKeys = Object.keys(incomingForm) || [];\n const isThere = (val) => {\n if (formKeys.indexOf(val) > -1) {\n return true;\n }\n return false;\n };\n let onlyChanged = {\n calendarPack: { currentValue: null },\n hospitalPack: { currentValue: null },\n limitedStability: { currentValue: null },\n price: { currentValue: null },\n subpackInfo: { currentValue: null },\n gtinCodes: { currentValue: null },\n legalCategory: { currentValue: null },\n quantity: { currentValue: null },\n };\n\n if (isThere(\"name\") && data.name) {\n data.name.currentValue = incomingForm.name;\n }\n // need relatedVMP label !!!! ---- waiting for BE.\n if (isThere(\"virtualProductId\") && data.relatedVMP) {\n data.relatedVMP.currentValue = {\n value: incomingForm.virtualProductId,\n label: incomingForm.virtualProductId,\n };\n }\n if (isThere(\"formulation\") && data.formulation) {\n data.formulation.currentValue = convertSavedValueToCurrentValue(\n \"formulation\",\n incomingForm.formulation || \"\",\n data,\n );\n }\n if (isThere(\"licensedRoutes\") && data.licensedRoutes) {\n data.licensedRoutes.currentValue = convertSavedValueToCurrentValue(\n \"licensedRoutes\",\n incomingForm.licensedRoutes,\n data,\n );\n }\n if (isThere(\"flavourCode\") && data.productFlavour) {\n data.productFlavour.currentValue = convertSavedValueToCurrentValue(\n \"productFlavour\",\n incomingForm.flavourCode,\n data,\n );\n }\n if (isThere(\"glutenFree\") && data.glutenFreeProduct) {\n data.glutenFreeProduct.currentValue = convertSavedValueToCurrentValue(\n \"glutenFreeProduct\",\n yesNoNAConverter(incomingForm.glutenFree),\n data,\n );\n }\n if (isThere(\"preservativeFree\") && data.preservativeFreeProduct) {\n data.preservativeFreeProduct.currentValue = convertSavedValueToCurrentValue(\n \"preservativeFreeProduct\",\n yesNoNAConverter(incomingForm.preservativeFree),\n data,\n );\n }\n if (isThere(\"sugarFree\") && data.sugarFreeProduct) {\n data.sugarFreeProduct.currentValue = convertSavedValueToCurrentValue(\n \"sugarFreeProduct\",\n yesNoNAConverter(incomingForm.sugarFree),\n data,\n );\n }\n if (isThere(\"cfcFree\") && data.cfcFreeProduct) {\n data.cfcFreeProduct.currentValue = convertSavedValueToCurrentValue(\n \"cfcFreeProduct\",\n yesNoNAConverter(incomingForm.cfcFree),\n data,\n );\n }\n if (isThere(\"controlledDrugCategoryCode\") && data.controlledDrugCategory) {\n data.controlledDrugCategory.currentValue = convertSavedValueToCurrentValue(\n \"controlledDrugCategory\",\n incomingForm.controlledDrugCategoryCode,\n data,\n );\n }\n if (isThere(\"licensingAuthorityCode\") && data.currentLicensingAuthority) {\n data.currentLicensingAuthority.currentValue = convertSavedValueToCurrentValue(\n \"currentLicensingAuthority\",\n incomingForm.licensingAuthorityCode,\n data,\n );\n }\n if (isThere(\"restrictionOnAvailability\") && data.restrictionsOnAvailability) {\n data.restrictionsOnAvailability.currentValue = convertSavedValueToCurrentValue(\n \"restrictionsOnAvailability\",\n incomingForm.restrictionOnAvailability,\n data,\n );\n }\n if (isThere(\"emaAdditionalMonitoring\") && data.emaAdditionalMonitoring) {\n data.emaAdditionalMonitoring.currentValue = convertSavedValueToCurrentValue(\n \"emaAdditionalMonitoring\",\n incomingForm.emaAdditionalMonitoring,\n data,\n );\n }\n if (isThere(\"ingredients\") && data.ingredients) {\n let newIngs = {};\n incomingForm.ingredients.forEach((item) => {\n newIngs[item.id] = {\n ingredientName: {\n currentValue: {\n label: item.name,\n value: item.id,\n },\n },\n bops: {\n currentValue: convertSavedValueToCurrentValue(\n \"bops\",\n item.basisOfPharmStrengthCode,\n data,\n ),\n },\n boss: {\n currentValue: {\n label: item.basisOfStrengthSubstanceId,\n value: item.basisOfStrengthSubstanceId,\n },\n },\n numerator: {\n currentValue: convertSavedValueToCurrentValue(\n \"numerator\",\n {\n value: item.numeratorValue,\n uom: item.numeratorUomCode,\n },\n data,\n ),\n },\n denominator: {\n currentValue: convertSavedValueToCurrentValue(\n \"denominator\",\n {\n value: item.denominatorStrengthValue,\n uom: item.denominatorStrengthUomCode,\n },\n data,\n ),\n },\n };\n });\n\n data.ingredients.currentValue = newIngs;\n }\n if (isThere(\"packs\") && data.packs) {\n let newPacks = {};\n incomingForm.packs.forEach((item) => {\n let findQuantity = {\n currentValue: convertSavedValueToCurrentValue(\n \"quantity\",\n {\n value: item.quantity,\n uom: item.unitOfMeasureCode,\n },\n data,\n ),\n };\n\n newPacks[\n findQuantity.currentValue.input + \"\" + findQuantity.currentValue.unit.label\n ] = {\n quantity: findQuantity,\n legalCategory: {\n currentValue: convertSavedValueToCurrentValue(\n \"legalCategory\",\n item.legalCategoryCode,\n data,\n ),\n },\n subpackInfo: { currentValue: item.subPackInformation },\n gtinCodes: { currentValue: item.gtinCodes },\n calendarPack: {\n currentValue: convertSavedValueToCurrentValue(\n \"calendarPack\",\n yesNoNAConverter(item.calendarPack),\n data,\n ),\n },\n hospitalPack: {\n currentValue: convertSavedValueToCurrentValue(\n \"hospitalPack\",\n yesNoNAConverter(item.hospitalPack),\n data,\n ),\n },\n limitedStability: {\n currentValue: convertSavedValueToCurrentValue(\n \"limitedStability\",\n yesNoNAConverter(item.limitedStability),\n data,\n ),\n },\n price: { currentValue: item.price },\n };\n });\n\n data.packs.currentValue = newPacks;\n }\n\n /**\n * UPDATE PACK VALS.\n */\n\n if (isThere(\"calendarPack\") && data.calendarPack) {\n data.calendarPack.currentValue = onlyChanged.calendarPack.currentValue = convertSavedValueToCurrentValue(\n \"calendarPack\",\n yesNoNAConverter(incomingForm.calendarPack),\n data,\n );\n }\n if (isThere(\"hospitalPack\") && data.hospitalPack) {\n data.hospitalPack.currentValue = onlyChanged.hospitalPack.currentValue = convertSavedValueToCurrentValue(\n \"hospitalPack\",\n yesNoNAConverter(incomingForm.hospitalPack),\n data,\n );\n }\n if (isThere(\"limitedStability\") && data.limitedStability) {\n data.limitedStability.currentValue = onlyChanged.limitedStability.currentValue = convertSavedValueToCurrentValue(\n \"limitedStability\",\n yesNoNAConverter(incomingForm.limitedStability),\n data,\n );\n }\n if (isThere(\"price\") && data.price) {\n data.price.currentValue = onlyChanged.price.currentValue = incomingForm.price + \"\";\n }\n if (isThere(\"subPackInformation\") && data.subpackInfo) {\n data.subpackInfo.currentValue = onlyChanged.subpackInfo.currentValue =\n incomingForm.subPackInformation;\n }\n if (isThere(\"gtinCodes\") && data.gtinCodes) {\n data.gtinCodes.currentValue = onlyChanged.gtinCodes.currentValue =\n incomingForm.gtinCodes;\n }\n if (isThere(\"legalCategoryCode\") && data.legalCategory) {\n data.legalCategory.currentValue = onlyChanged.legalCategory.currentValue = convertSavedValueToCurrentValue(\n \"legalCategory\",\n incomingForm.legalCategoryCode,\n data,\n );\n }\n if (isThere(\"quantity\") && data.quantity) {\n data.quantity.currentValue = onlyChanged.quantity.currentValue = convertSavedValueToCurrentValue(\n \"quantity\",\n {\n value: incomingForm.quantity,\n uom: incomingForm.unitOfMeasureCode,\n },\n data,\n );\n }\n\n /**\n * LOG THEM.\n */\n\n if (\n !process.env.NODE_ENV &&\n process.env.NODE_ENV === \"development\" &&\n showErrorLogsOnConsole\n ) {\n console.info(\"nhsFormConverter worked.\");\n console.info(\"incomingForm>\", incomingForm);\n console.info(\"resultData>\", data);\n }\n\n return {\n data: data,\n onlyChanged: onlyChanged,\n };\n };\n\n const findError = (fieldOrfieldName, currentValue) => {\n let errorText = [];\n\n if (!fieldOrfieldName) {\n return errorText;\n }\n\n let field;\n if (typeof fieldOrfieldName === \"object\") {\n field = fieldOrfieldName;\n } else {\n if (!data) {\n return errorText;\n }\n field = data[fieldOrfieldName];\n }\n\n if (!field?.validationRules || field.validationRules.length === 0) {\n return errorText;\n }\n\n field.validationRules.forEach((rule) => {\n let validationResult = rule({\n typeIdentifier: typeIdentifier,\n DATA: currentValue,\n hideFromUI: field.hideFromUI,\n });\n if (validationResult !== true) {\n errorText.push(validationResult);\n }\n });\n\n return errorText;\n };\n const findAllErrors = (data) => {\n Object.entries(data).map(([key, item]) => {\n let validationRules = item.validationRules;\n if (!validationRules) {\n return null;\n }\n\n let errorText = [];\n\n validationRules.forEach((rule) => {\n let validationResult = rule({\n keyName: key,\n DATA: item.currentValue,\n setHeaderTitles: setHeaderTitles,\n typeIdentifier: typeIdentifier,\n token: userState.accessToken,\n hideFromUI: item.hideFromUI,\n });\n if (validationResult !== true) {\n errorText.push(validationResult);\n }\n });\n\n data[key].errorText = errorText;\n\n return null;\n });\n\n return data;\n };\n const areTheyEqual = (item1, item2) => {\n const typeOfItem1 = typeIdentifier(item1);\n const typeOfItem2 = typeIdentifier(item2);\n if (typeOfItem1 !== typeOfItem2) {\n return false;\n }\n\n if (typeOfItem1 === \"string\" || typeOfItem1 === \"number\" || typeOfItem1 === \"boolean\") {\n if (item1 !== item2) {\n return false;\n }\n } else if (typeOfItem1 === \"array\") {\n if (!_.isEqual(item1.sort(), item2.sort())) {\n return false;\n }\n } else if (typeOfItem1 === \"object\") {\n if (!_.isEqual(item1, item2)) {\n return false;\n }\n }\n\n return true;\n };\n\n /**\n * GET and ORGANISE ALL DATA\n */\n useLayoutEffect(() => {\n // go Error page if page doesn't have enough data.\n\n if (\n (action !== \"new\" && action !== \"edit\" && action !== \"view\") ||\n (action === \"new\" &&\n submissionType !== \"NewProduct\" &&\n submissionType !== \"NewPack\" &&\n submissionType !== \"UpdateProduct\" &&\n submissionType !== \"UpdatePack\" &&\n submissionType !== \"MassPriceUpdate\") ||\n ((action === \"edit\" || action === \"view\") && !ID)\n ) {\n history.push(\"/error/404\");\n }\n\n let newData;\n let savedData;\n\n let currentID = ID;\n let currentAction = action;\n let currentSubmissionType = submissionType;\n let currentSubmissionID = submissionID;\n let currentStatus = \"Draft\";\n let currentPageName;\n let newDataKeys;\n let newInternalState = 1;\n let inComingPack;\n\n // STEP 1 - AWAIT\n // GET SAVED DATA if action === edit or view\n const step1 = async () => {\n let retrievedData;\n if (action === \"new\" && submissionType === \"MassPriceUpdate\") {\n const newValue = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callMassPriceUpdate\",\n token: userState.accessToken,\n apiNeeds: {\n companyId: userState.selectedCompany?.id,\n },\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n\n retrievedData = {\n type: submissionType,\n data: {\n prices: newValue.data,\n },\n };\n } else if (\n (action === \"view\" || action === \"edit\") &&\n submissionType === \"MassPriceUpdate\"\n ) {\n setIsLoading(false);\n return;\n } else if (action === \"view\" || action === \"edit\") {\n retrievedData = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callASubmission\",\n token: userState.accessToken,\n apiNeeds: {\n ID: ID,\n submissionID: submissionID,\n companyId: userState.selectedCompany?.id,\n },\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n\n if (!retrievedData || retrievedData.data.length === 0) {\n history.push(\"/error/404\");\n }\n\n if (retrievedData.type === \"UpdatePack\" && retrievedData.data.relatedPack) {\n inComingPack = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callAMPP_idSearch\",\n token: userState.accessToken,\n apiNeeds: {\n value: retrievedData.data.relatedPack,\n },\n });\n }\n }\n\n if (retrievedData) {\n savedData = retrievedData;\n currentID = retrievedData.id;\n currentSubmissionType = retrievedData.type;\n currentSubmissionID = retrievedData.submissionId;\n currentStatus = retrievedData.status || \"Draft\";\n }\n };\n\n // STEP 2\n // GET FORM FIELDS DEPENDING ON SUBMISSION TYPE (NewProduct, UpdatePack etc.)\n const step2 = async () => {\n switch (currentSubmissionType) {\n case \"NewProduct\":\n newData = _.cloneDeep(NewProduct(await getProductLookups()));\n break;\n case \"NewPack\":\n newData = _.cloneDeep(NewPack(await getPackLookups()));\n break;\n case \"UpdateProduct\":\n newData = _.cloneDeep(UpdateProduct(await getProductLookups()));\n break;\n case \"UpdatePack\":\n newData = _.cloneDeep(UpdatePack(await getPackLookups()));\n break;\n case \"MassPriceUpdate\":\n newData = _.cloneDeep(MassPriceUpdate);\n break;\n default:\n history.push(\"/error/404\");\n return;\n }\n };\n\n const getProductLookups = async () => {\n const formulations = await getLookup(\"callFormulation\");\n const licensedRoutes = await getLookup(\"callLicensedRoutes\");\n const flavours = await getLookup(\"callFlavours\")\n const controlledDrugCategories = await getLookup(\"callControlledDrugCategories\")\n const currentLicensing = await getLookup(\"callCurrentLicensing\")\n const restrictions = await getLookup(\"callRestrictions\")\n const bops = await getLookup(\"callBops\")\n const units = await getLookup(\"callUnits\");\n const legalCategories = await getLookup(\"callLegalCategory\")\n return {formulations, licensedRoutes, flavours, controlledDrugCategories, currentLicensing, restrictions, bops, units, legalCategories};\n }\n\n const getPackLookups = async () => {\n const units = await getLookup(\"callUnits\");\n const legalCategories = await getLookup(\"callLegalCategory\")\n return {units, legalCategories};\n }\n\n const getLookup = async (apiShortName) => await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: apiShortName,\n token: userState.accessToken,\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n\n // STEP 3\n // UPDATE newData via savedData if action is VIEW or EDIT\n const step3 = () => {\n const processData = () => {\n if (!savedData) {\n return;\n }\n\n newDataKeys = Object.keys(newData);\n if (newDataKeys.indexOf(\"name\") > -1) {\n newData.name.currentValue = savedData.title;\n }\n let list = Object.keys(savedData.data);\n list.forEach((key) => {\n if (newDataKeys.indexOf(key) > -1) {\n newData[key].currentValue = convertSavedValueToCurrentValue(\n newData[key],\n savedData.data[key],\n newData,\n );\n }\n });\n };\n\n if (action === \"new\") {\n processData();\n } else if (action === \"view\" || action === \"edit\") {\n processData();\n\n if (currentSubmissionType === \"MassPriceUpdate\") {\n if (action === \"view\") {\n newData.prices.isReadOnly = true;\n } else {\n newData.prices.isReadOnly = false;\n }\n }\n\n if (currentSubmissionType === \"UpdateProduct\") {\n if (_.isEmpty(newData.relatedProduct.currentValue)) {\n newInternalState = 1;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n } else {\n if (newData.discontinueProduct?.currentValue?.label === \"Yes\") {\n newInternalState = 2;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = false;\n }\n } else if (newData.discontinueProduct?.currentValue?.label === \"No\") {\n newInternalState = 3;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n } else {\n newInternalState = 2;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n }\n }\n }\n\n if (currentSubmissionType === \"UpdatePack\") {\n let nhsConverter = convertNhsFormToCurrentValue(newData, inComingPack);\n inComingPack = _.cloneDeep(nhsConverter.onlyChanged);\n\n if (\n inComingPack?.quantity?.currentValue?.input &&\n inComingPack?.quantity?.currentValue?.unit?.label\n ) {\n newData.relatedPack.currentValue.label =\n inComingPack.quantity.currentValue.input +\n \" \" +\n inComingPack.quantity.currentValue.unit.label;\n }\n\n if (_.isEmpty(newData.relatedProduct.currentValue)) {\n newInternalState = 1;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n } else if (_.isEmpty(newData.relatedPack.currentValue)) {\n newInternalState = 2;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n } else {\n if (newData.discontinuePack?.currentValue?.label === \"Yes\") {\n newInternalState = 3;\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = false;\n newData.discontinueQuantity.hideFromUI = false;\n }\n } else if (newData.discontinuePack?.currentValue?.label === \"No\") {\n newInternalState = 4;\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n } else {\n newInternalState = 3;\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n }\n }\n }\n }\n };\n\n // STEP 4\n // FIND ERRORS\n const step4 = () => {\n if (savedData) {\n if (\n currentSubmissionType === \"UpdateProduct\" ||\n currentSubmissionType === \"UpdatePack\"\n ) {\n const savedIngredients = savedData[\"ingredients\"] || [];\n const hasNoSavedIngredients = savedIngredients.length === 0;\n\n const target = newData.ingredients || {};\n const noValidation = [];\n\n if (hasNoSavedIngredients) {\n target.validationRules = noValidation;\n }\n }\n }\n\n newData = findAllErrors(newData);\n };\n\n // LAST STEP, STEP 5\n // UPDATE STATES\n\n const step5 = () => {\n disableIsLoadingWithDelay();\n if (action === \"new\") {\n setHeaderTitles({\n id: null,\n submissionType: addSpaces(currentSubmissionType),\n productName: null,\n status: \"Draft\",\n submissionId: null,\n currentAction: currentAction,\n });\n } else {\n setHeaderTitles({\n id: ID,\n submissionType: addSpaces(savedData.type),\n productName: savedData.title,\n status: currentStatus,\n submissionId: submissionID,\n currentAction: currentAction,\n });\n }\n currentPageName =\n \"handlePage/\" +\n currentAction +\n \"/\" +\n currentSubmissionType +\n \"/\" +\n currentID;\n //currentSubmissionID;\n setPageName(currentPageName);\n setSidebarBehavior({ position: \"narrow\", content: \"none\" });\n\n setSettings({\n isThereAnyChange: false,\n action: currentAction,\n submissionType: currentSubmissionType,\n ID: currentID,\n submissionID: currentSubmissionID,\n name: savedData ? savedData.title : \"\",\n status: currentStatus,\n savedData: savedData,\n internalState: newInternalState,\n inComingPack: inComingPack,\n });\n setData(newData);\n };\n\n // RUN FUNCS\n const runFuncs = async () => {\n await step1();\n await step2();\n step3();\n step4();\n step5();\n };\n runFuncs();\n }, [ID, submissionType, submissionID, action]);\n \n /**\n * UPDATE DATA\n */\n const updateData = async (fieldName, newDataObj, propName = \"currentValue\") => {\n if (!data || !settings) {\n return;\n }\n let currentSubmissionType = settings.submissionType || \"\";\n\n /**\n * CLONE MAIN DATA and CHANGE INCOMING VALUES\n */\n let errorText = findError(fieldName, newDataObj);\n let newData = _.cloneDeep(data);\n let newSettings = _.cloneDeep(settings);\n\n newData[fieldName] = {\n ...newData[fieldName],\n [propName]: newDataObj,\n ...(propName === \"currentValue\" && {\n errorText: errorText,\n }),\n };\n\n /**\n * CHECK IF THERE IS ANY CHANGE FOR ONE TIME\n */\n if (\n !settings.isThereAnyChange &&\n data[fieldName].dontSendToApi !== true &&\n propName === \"currentValue\"\n ) {\n newSettings.isThereAnyChange = true;\n }\n\n /**\n * UNIQUE FUNCTIONS DEPENDING ON SUBMISSION TYPE\n */\n const toggleSaveIngredientButton = () => {\n const relatedFieldNames = [\n \"ingredientName\",\n \"bops\",\n \"boss\",\n \"numerator\",\n \"denominator\",\n ];\n if (!data.saveIngredientButton || relatedFieldNames.indexOf(fieldName) === -1) {\n return;\n } else if (errorText.length > 0) {\n newData.saveIngredientButton.buttonType = \"deactive\";\n } else {\n let newArr = relatedFieldNames.filter((item) => {\n if (item !== fieldName) {\n return data[item]?.errorText?.length !== 0;\n } else {\n return null;\n }\n });\n\n if (newArr.length > 0) {\n newData.saveIngredientButton.buttonType = \"deactive\";\n } else {\n newData.saveIngredientButton.buttonType = \"\";\n }\n }\n };\n const toggleSavePackButton = () => {\n const relatedFieldNames = [\n \"quantity\",\n \"legalCategory\",\n \"subpackInfo\",\n \"gtinCodes\",\n \"calendarPack\",\n \"hospitalPack\",\n \"limitedStability\",\n \"price\",\n ];\n if (!data.savePackButton || relatedFieldNames.indexOf(fieldName) === -1) {\n return;\n } else if (errorText.length > 0) {\n newData.savePackButton.buttonType = \"deactive\";\n } else {\n let newArr = relatedFieldNames.filter((item) => {\n if (item !== fieldName) {\n const errorCount = data[item]?.errorText?.length || 0;\n return errorCount !== 0;\n } else {\n return null;\n }\n });\n\n if (newArr.length > 0) {\n newData.savePackButton.buttonType = \"deactive\";\n } else {\n newData.savePackButton.buttonType = \"\";\n }\n }\n };\n const relatedProductChanged = async () => {\n if (\n !data.relatedProduct ||\n fieldName !== \"relatedProduct\" ||\n propName !== \"currentValue\" ||\n areTheyEqual(newDataObj, data.relatedProduct.currentValue)\n ) {\n return;\n }\n\n let inComingValues = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callAMP_idSearch\",\n token: userState.accessToken,\n apiNeeds: {\n value: newDataObj.value,\n },\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n newData = _.cloneDeep(convertNhsFormToCurrentValue(newData, inComingValues).data);\n newData = findAllErrors(newData);\n newSettings.internalState = 2;\n\n setIsLoading(false);\n };\n const discontinueProductChanged = () => {\n if (_.isEmpty(newData.relatedProduct?.currentValue)) {\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n return;\n }\n\n if (newData.discontinueProduct?.currentValue?.label === \"Yes\") {\n newSettings.internalState = 2;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = false;\n }\n } else if (newData.discontinueProduct?.currentValue?.label === \"No\") {\n newSettings.internalState = 3;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n } else {\n newSettings.internalState = 2;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n }\n }\n\n newData = findAllErrors(newData);\n };\n const discontinuePackChanged = () => {\n if (\n _.isEmpty(newData.relatedProduct?.currentValue) ||\n _.isEmpty(newData.relatedPack?.currentValue)\n ) {\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n return;\n }\n\n if (newData.discontinuePack?.currentValue?.label === \"Yes\") {\n newSettings.internalState = 3;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = false;\n newData.discontinueQuantity.hideFromUI = false;\n }\n } else if (newData.discontinuePack?.currentValue?.label === \"No\") {\n newSettings.internalState = 4;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n } else {\n newSettings.internalState = 3;\n\n if (newData.discontinueReason) {\n newData.discontinueReason.hideFromUI = true;\n newData.discontinueQuantity.hideFromUI = true;\n }\n }\n\n newData = findAllErrors(newData);\n };\n\n const relatedProductChangedForUpdatePack = async () => {\n if (\n !data.relatedProduct ||\n fieldName !== \"relatedProduct\" ||\n propName !== \"currentValue\" ||\n areTheyEqual(newDataObj, data.relatedProduct.currentValue)\n ) {\n return;\n }\n\n let inComingValues = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callAMPP\",\n token: userState.accessToken,\n apiNeeds: {\n companyId: userState.selectedCompany?.id,\n ampId: newDataObj.value,\n },\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n\n let newPackObj = [];\n\n inComingValues.data.forEach((item) => {\n newPackObj.push({\n value: item.id,\n label: item.name,\n });\n });\n\n newData.relatedPack.optionValue = newPackObj;\n newData.relatedPack.currentValue = undefined;\n\n newSettings.internalState = 2;\n setIsLoading(false);\n };\n const relatedPackChanged = async () => {\n if (\n !data.relatedPack ||\n fieldName !== \"relatedPack\" ||\n propName !== \"currentValue\" ||\n areTheyEqual(newDataObj, data.relatedPack.currentValue)\n ) {\n return;\n }\n\n let inComingValues = await FetchAPI({\n showErrorLogsOnConsole: showErrorLogsOnConsole,\n history: history,\n apiShortName: \"callAMPP_idSearch\",\n token: userState.accessToken,\n apiNeeds: {\n value: newDataObj.value,\n },\n setIsLoading: (boo) => {\n setIsLoading(boo);\n },\n });\n\n let nhsConverter = convertNhsFormToCurrentValue(newData, inComingValues);\n\n newData = _.cloneDeep(nhsConverter.data);\n newSettings.inComingPack = _.cloneDeep(nhsConverter.onlyChanged);\n\n newData = findAllErrors(newData);\n newSettings.internalState = 3;\n setIsLoading(false);\n };\n const massPriceUpdateChanged = () => {\n const noOfUpdatedPrice = newData.prices.currentValue.filter(item => item.newPrice > 0).length;\n newSettings.isThereAnyChange = noOfUpdatedPrice > 0;\n };\n\n switch (currentSubmissionType) {\n case \"NewProduct\":\n toggleSaveIngredientButton();\n toggleSavePackButton();\n break;\n case \"UpdateProduct\":\n await relatedProductChanged();\n discontinueProductChanged();\n toggleSavePackButton();\n break;\n case \"UpdatePack\":\n await relatedProductChangedForUpdatePack();\n await relatedPackChanged();\n discontinuePackChanged();\n break;\n case \"MassPriceUpdate\":\n massPriceUpdateChanged();\n break;\n default:\n break;\n }\n\n setData(newData);\n setSettings(newSettings);\n };\n const silentUpdateData = (fieldName, newDataObj, propName = \"currentValue\") => {\n if (!data) {\n return;\n }\n\n let currentValue = data[fieldName][propName];\n if (!areTheyEqual(currentValue, newDataObj)) {\n setData({\n ...data,\n [fieldName]: {\n ...data[fieldName],\n [propName]: newDataObj,\n },\n });\n }\n };\n\n /**\n * UPDATE HEADER TITLE\n */\n useLayoutEffect(() => {\n if (!data || !settings) {\n return;\n }\n\n let newHeaderTitle = \"\";\n\n if (settings.submissionType === \"NewProduct\" && data.name?.currentValue) {\n newHeaderTitle = data.name.currentValue;\n } else if (\n (settings.submissionType === \"NewPack\" || settings.submissionType === \"UpdatePack\") &&\n data.relatedProduct?.currentValue?.label &&\n data.quantity?.currentValue?.unit?.label\n ) {\n newHeaderTitle =\n data.relatedProduct.currentValue.label +\n \" \" +\n data.quantity.currentValue.input +\n \" \" +\n data.quantity.currentValue.unit.label;\n } else if (\n settings.submissionType === \"UpdateProduct\" &&\n data.relatedProduct?.currentValue?.label\n ) {\n newHeaderTitle = data.relatedProduct.currentValue.label;\n } else if (\n settings.submissionType === \"MassPriceUpdate\" &&\n data.prices?.currentValue?.length\n ) {\n const prices = data.prices?.currentValue || [];\n const count = prices.filter((item) => item.newPrice !== undefined && item.newPrice > 0).length;\n\n newHeaderTitle = `${count} Prices Updated`;\n } else {\n newHeaderTitle = settings.name;\n }\n\n if (setHeaderTitles.productName !== newHeaderTitle) {\n setHeaderTitles((prev) => ({\n ...prev,\n productName: newHeaderTitle,\n }));\n }\n }, [data, settings?.submissionType]);\n\n /**\n * DEFINE SECTIONS for ANCHOR POINTS\n */\n useLayoutEffect(() => {\n if (!data) {\n return;\n }\n let newSections = [];\n\n Object.keys(data).forEach((item) => {\n if (data[item].isASection) {\n newSections.push(item);\n }\n });\n setSections(newSections);\n }, [data]);\n\n /**\n * SCROLL TO ERROR FIELD\n */\n const errorFieldNameClicked = (fieldName) => {\n let element = document.getElementById(fieldName);\n window.scrollTo(0, element.offsetTop + 75);\n };\n\n /**\n * ON EXTERNAL CLICKS CALLBACK\n */\n const onExternalClick = (callBack, additionalData) => {\n if (callBack) {\n callBack(\n {\n data: data,\n setData: setData,\n updateData: updateData,\n silentUpdateData: silentUpdateData,\n settings: settings,\n setSettings: setSettings,\n findError: findError,\n },\n additionalData,\n );\n }\n };\n\n /**\n * USE CONSOL to SEE DATA or SETTINGS for DEVELOPMENT\n */\n if (!process.env.NODE_ENV || process.env.NODE_ENV === \"development\" || showErrorLogsOnConsole) {\n window.data = data;\n window.settings = settings;\n }\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n\n if (!data || !settings || isLoading) {\n return null;\n }\n\n return (\n
\n );\n};\n","import React from \"react\";\nimport { useLayoutEffect, useContext } from \"react\";\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\n\nexport const About = (props) => {\n const thisPageName = \"aboutPage\";\n const {\n pageName,\n setPageName,\n setSidebarBehavior,\n disableIsLoadingWithDelay,\n setIsLoading,\n } = useContext(GlobalContext);\n\n /**\n * SET PAGE NAME and SIDEBAR BEHAVIOUR IN MOUNT AND REMOVE ISLOADING\n * @description sets pageName and sidebarBehaviour in globalStore\n */\n useLayoutEffect(() => {\n setIsLoading(true);\n setPageName(thisPageName);\n setSidebarBehavior({ position: \"narrow\", content: \"about\" });\n disableIsLoadingWithDelay();\n }, [pageName]);\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n return (\n
\n
About emc in demand
\n\n
\n
\n Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed\n do eiusmod tempor incididunt ut labore et dolore magna\n aliqua. In dictum non consectetur a erat nam at lectus urna.\n Molestie a iaculis at erat pellentesque adipiscing commodo\n elit at. Rhoncus mattis rhoncus urna neque viverra justo nec\n ultrices. Pellentesque eu tincidunt tortor aliquam nulla.\n Morbi leo urna molestie at elementum. Quis hendrerit dolor\n magna eget est lorem.\n
\n\n
\n Hac habitasse platea dictumst quisque sagittis purus sit\n amet. Lectus magna fringilla urna porttitor rhoncus dolor\n purus. Orci a scelerisque purus semper eget. Velit egestas\n dui id ornare arcu. Venenatis urna cursus eget nunc\n scelerisque viverra mauris in. Sodales neque sodales ut\n etiam sit amet nisl. Vitae semper quis lectus nulla at\n volutpat diam ut. Facilisi etiam dignissim diam quis enim\n lobortis scelerisque fermentum dui. In hendrerit gravida\n rutrum quisque non. Est ullamcorper eget nulla facilisi\n etiam. Massa tincidunt dui ut ornare lectus sit amet est\n placerat.\n
\n
\n\n
Legal
\n\n
\n
\n Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed\n do eiusmod tempor incididunt ut labore et dolore magna\n aliqua. In dictum non consectetur a erat nam at lectus urna.\n Molestie a iaculis at erat pellentesque adipiscing commodo\n elit at. Rhoncus mattis rhoncus urna neque viverra justo nec\n ultrices. Pellentesque eu tincidunt tortor aliquam nulla.\n Morbi leo urna molestie at elementum. Quis hendrerit dolor\n magna eget est lorem.\n
\n\n
\n Hac habitasse platea dictumst quisque sagittis purus sit\n amet. Lectus magna fringilla urna porttitor rhoncus dolor\n purus. Orci a scelerisque purus semper eget. Velit egestas\n dui id ornare arcu. Venenatis urna cursus eget nunc\n scelerisque viverra mauris in. Sodales neque sodales ut\n etiam sit amet nisl. Vitae semper quis lectus nulla at\n volutpat diam ut. Facilisi etiam dignissim diam quis enim\n lobortis scelerisque fermentum dui. In hendrerit gravida\n rutrum quisque non. Est ullamcorper eget nulla facilisi\n etiam. Massa tincidunt dui ut ornare lectus sit amet est\n placerat.\n
\n
\n
\n );\n};\n","import React from \"react\";\nimport { useLayoutEffect, useContext } from \"react\";\nimport { useHistory } from \"react-router-dom\";\nimport { GlobalContext } from \"contexts/GlobalStore.js\";\nimport { Button } from \"components/FormElements/Button/Button.js\";\nimport \"./ErrorPages.scss\";\n\nexport const ErrorPages = (props) => {\n const type = String(\n props?.type ? props.type : props?.match?.params?.type ? props.match.params.type : \"unknown\",\n );\n const {\n pageName,\n setPageName,\n setSidebarBehavior,\n goToHome,\n setIsLoading,\n disableIsLoadingWithDelay,\n } = useContext(GlobalContext);\n const history = useHistory();\n const thisPageName = \"errorPage\";\n\n /**\n * SET PAGE NAME and SIDEBAR BEHAVIOUR IN MOUNT AND REMOVE ISLOADING\n * @description sets pageName and sidebarBehaviour in globalStore\n */\n useLayoutEffect(() => {\n setIsLoading(true);\n setPageName(thisPageName);\n setSidebarBehavior({ position: \"noSidebar\", content: \"hide\" });\n disableIsLoadingWithDelay();\n history.replace(\"/error/\" + type);\n }, [pageName]);\n\n //-------------------------------------------------------------\n // \t\tRETURN\n //-------------------------------------------------------------\n return (\n
\n {\n goToHome();\n }}\n />\n\n
Sorry to say, but...
\n
\n {type === \"403\" ? (\n
\n You do not have access to the page you have requested.\n \n Please contact Datapharm.\n
\n You have insufficient permissions to access emc med data dm+d.\n \n \n Please contact the service desk at servicedesk@datapharm.com\n \n or call us on 01372 371 44\n
\n ) : (\n
\n There is something wrong.\n \n Please try again or contact your admin.\n
\n )\n ) : null}\n \n );\n};\n","import React, { useContext } from \"react\";\nimport { Switch, Route, useHistory } from \"react-router-dom\";\n\nimport Telemetry from './Telemetry';\nimport { PrivateRoute } from \"./contexts/PrivateRoute.js\";\nimport { AuthPages } from \"./contexts/AuthPages.js\";\n\nimport { IsLoading } from \"./components/IsLoading/IsLoading.js\";\nimport { BackEmblem } from \"./components/BackEmblem/BackEmblem.js\";\nimport { Sidebar } from \"./components/Sidebar/Sidebar.js\";\nimport { Header } from \"./components/Header/Header.js\";\nimport { Home } from \"./pages/Home/Home.js\";\nimport { HandleSub } from \"./pages/HandleSub/HandleSub.js\";\nimport { About } from \"./pages/About/About.js\";\nimport { ErrorPages } from \"./pages/ErrorPages/ErrorPages.js\";\nimport { LoggedOut } from \"./pages/LoggedOut/LoggedOut.js\";\nimport { MobileMenu } from \"./components/MobileMenu/MobileMenu.js\";\nimport { List } from \"./pages/List/List.js\";\n\nimport { UserContext } from \"contexts/UserStore.js\";\n\nimport { version } from \"../package.json\";\n\nimport \"./css_app.scss\";\n\nexport const App = () => {\n\n const { userState } = useContext(UserContext);\n const history = useHistory();\n\n if (userState && userState.currentPermLevel < 1) {\n history.push(\"/logout\");\n }\n\n return (\n\n
\n
\n {version}\n
\n {/* ISLOADING */}\n \n\n {/* BACK EMBLEM */}\n \n\n {/* SIDEBAR */}\n \n\n {/* CONTENT ZONE is STARTING */}\n
\n {/* MOBILE HEADER WILL PLACE HERE ?!?!? */}\n \n\n {/* HEADER */}\n \n\n \n {/* SWITCH to PAGE CONTENT */}\n \n \n \n\n \n \n \n \n\n \n \n\n \n\n } />\n \n \n
\n
\n );\n};\n","import \"babel-polyfill\";\nimport \"core-js/features/url-search-params\";\n\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { BrowserRouter as Router } from \"react-router-dom\";\nimport * as serviceWorker from \"./serviceWorker\";\n\nimport GlobalStore from \"./contexts/GlobalStore.js\";\nimport ClientStore from \"./contexts/ClientStore.js\";\nimport UserStore from \"./contexts/UserStore.js\";\nimport { App } from \"./App\";\nimport FeatureFlagsStore from \"contexts/FeatureContext\";\nimport { AppInsightsContext } from '@microsoft/applicationinsights-react-js';\nimport { reactPlugin } from './Telemetry';\n\nReactDOM.render(\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t,\n\tdocument.getElementById(\"root\"),\n);\n\nserviceWorker.unregister();\n","module.exports = __webpack_public_path__ + \"static/media/formClose.dba9f898.svg\";"],"sourceRoot":""}