diff --git a/apps/smart-forms-app/cypress/e2e/auth.cy.ts b/apps/smart-forms-app/cypress/e2e/auth.cy.ts
index 75b6e0c26..54676d608 100644
--- a/apps/smart-forms-app/cypress/e2e/auth.cy.ts
+++ b/apps/smart-forms-app/cypress/e2e/auth.cy.ts
@@ -22,11 +22,8 @@ describe('launch app in SMART Launcher', () => {
const launchPage = 'http://localhost:5173/launch';
it('from launch page, redirect to picker', () => {
- cy.intercept('/').as('homepage');
-
cy.visit(launchPage);
- cy.wait('@homepage').its('response.statusCode').should('eq', 200);
- cy.location('pathname').should('eq', '/');
+ cy.getByData('button-unlaunched-state');
});
});
diff --git a/apps/smart-forms-app/cypress/e2e/populate.cy.ts b/apps/smart-forms-app/cypress/e2e/populate.cy.ts
index 133da8818..4db624f55 100644
--- a/apps/smart-forms-app/cypress/e2e/populate.cy.ts
+++ b/apps/smart-forms-app/cypress/e2e/populate.cy.ts
@@ -50,7 +50,7 @@ describe('populate form', () => {
it('form items in Patient Details tab have expected populated answers', () => {
cy.goToPatientDetailsTab();
- cy.getByData('q-item-string-box').eq(0).find('input').should('have.value', 'Benito Lucio');
+ cy.getByData('q-item-string-box').eq(0).find('input').should('have.value', 'Lucio, Benito Mr.');
cy.getByData('q-item-date-box').eq(0).find('input').should('have.value', '18/08/1936');
cy.getByData('q-item-choice-radio-answer-value-set-box')
@@ -97,7 +97,7 @@ describe('populate form', () => {
cy.previewForm();
cy.getByData('response-item-text').contains('Name');
- cy.getByData('response-item-answer').contains('Benito Lucio');
+ cy.getByData('response-item-answer').contains('Lucio, Benito Mr.');
cy.getByData('response-item-text').contains('Date of birth');
cy.getByData('response-item-answer').contains('18/08/1936');
diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json
index 6844d32c5..45759b2e2 100644
--- a/apps/smart-forms-app/package.json
+++ b/apps/smart-forms-app/package.json
@@ -23,6 +23,7 @@
"@sentry/react": "^7.58.1",
"@sentry/tracing": "^7.57.0",
"@tanstack/react-query": "^4.29.5",
+ "@tanstack/react-table": "^8.9.3",
"allotment": "^1.19.0",
"dayjs": "^1.11.7",
"fhirclient": "^2.5.2",
diff --git a/apps/smart-forms-app/src/components/Button/UnlaunchedButton.tsx b/apps/smart-forms-app/src/components/Button/UnlaunchedButton.tsx
new file mode 100644
index 000000000..8768c139f
--- /dev/null
+++ b/apps/smart-forms-app/src/components/Button/UnlaunchedButton.tsx
@@ -0,0 +1,21 @@
+import { Button } from '@mui/material';
+import { useNavigate } from 'react-router-dom';
+
+function UnlaunchedButton() {
+ const navigate = useNavigate();
+
+ return (
+
+ );
+}
+
+export default UnlaunchedButton;
diff --git a/apps/smart-forms-app/src/components/Header/DesktopHeader.tsx b/apps/smart-forms-app/src/components/Header/DesktopHeaderIcons.tsx
similarity index 78%
rename from apps/smart-forms-app/src/components/Header/DesktopHeader.tsx
rename to apps/smart-forms-app/src/components/Header/DesktopHeaderIcons.tsx
index aab78e8fc..14d72e432 100644
--- a/apps/smart-forms-app/src/components/Header/DesktopHeader.tsx
+++ b/apps/smart-forms-app/src/components/Header/DesktopHeaderIcons.tsx
@@ -15,23 +15,26 @@
* limitations under the License.
*/
-import { Avatar, Box, useTheme } from '@mui/material';
+import { Avatar, Box, Typography, useTheme } from '@mui/material';
import { AccountDetailsTypography, AccountNameTypography } from '../Typography/Typography.tsx';
import { constructName } from '../../features/smartAppLaunch/utils/launchContext.ts';
import MedicalServicesIcon from '@mui/icons-material/MedicalServices';
import useConfigStore from '../../stores/useConfigStore.ts';
-function DesktopHeader() {
+function DesktopHeaderIcons() {
const user = useConfigStore((state) => state.user);
const theme = useTheme();
return (
-
-
+
+
+
+ User
+
{user && user.gender ? : null}
@@ -39,4 +42,4 @@ function DesktopHeader() {
);
}
-export default DesktopHeader;
+export default DesktopHeaderIcons;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardHeader/DashboardHeader.tsx b/apps/smart-forms-app/src/components/Header/GenericHeader.tsx
similarity index 52%
rename from apps/smart-forms-app/src/features/dashboard/components/DashboardHeader/DashboardHeader.tsx
rename to apps/smart-forms-app/src/components/Header/GenericHeader.tsx
index 5eb2fa1f4..e1a7ebfc5 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardHeader/DashboardHeader.tsx
+++ b/apps/smart-forms-app/src/components/Header/GenericHeader.tsx
@@ -15,30 +15,22 @@
* limitations under the License.
*/
-import { Box, Stack } from '@mui/material';
-import Iconify from '../../../../components/Iconify/Iconify.tsx';
-import { useTheme } from '@mui/material/styles';
-import DesktopHeader from '../../../../components/Header/DesktopHeader.tsx';
-import useResponsive from '../../../../hooks/useResponsive.ts';
-import Logo from '../../../../components/Logos/Logo.tsx';
-import { LogoWrapper } from '../../../../components/Logos/Logo.styles.ts';
-import {
- MenuIconButton,
- StyledRoot,
- StyledToolbar
-} from '../../../../components/Header/Header.styles.ts';
-import MobileHeaderWithQuestionnaire from '../../../../components/Header/MobileHeaderWithoutQuestionnaire.tsx';
+import { Box } from '@mui/material';
+import Iconify from '../Iconify/Iconify.tsx';
+import useResponsive from '../../hooks/useResponsive.ts';
+import Logo from '../Logos/Logo.tsx';
+import { LogoWrapper } from '../Logos/Logo.styles.ts';
+import { MenuIconButton, StyledRoot, StyledToolbar } from './Header.styles.ts';
import { memo } from 'react';
+import HeaderIcons from './HeaderIcons.tsx';
interface DashboardHeaderProps {
onOpenNav: () => void;
}
-const DashboardHeader = memo(function DashboardHeader(props: DashboardHeaderProps) {
+const GenericHeader = memo(function DashboardHeader(props: DashboardHeaderProps) {
const { onOpenNav } = props;
- const theme = useTheme();
-
const isDesktop = useResponsive('up', 'lg');
return (
@@ -53,21 +45,13 @@ const DashboardHeader = memo(function DashboardHeader(props: DashboardHeaderProp
) : null}
+
-
- {isDesktop ? : }
-
+
);
});
-export default DashboardHeader;
+export default GenericHeader;
diff --git a/apps/smart-forms-app/src/components/Header/Header.styles.ts b/apps/smart-forms-app/src/components/Header/Header.styles.ts
index fc4592dc5..aee077fe5 100644
--- a/apps/smart-forms-app/src/components/Header/Header.styles.ts
+++ b/apps/smart-forms-app/src/components/Header/Header.styles.ts
@@ -18,9 +18,9 @@
import { alpha, styled } from '@mui/material/styles';
import { AppBar, IconButton, Toolbar } from '@mui/material';
-const NAV_WIDTH = 240;
-const HEADER_MOBILE = 64;
-const HEADER_DESKTOP = 72;
+export const NAV_WIDTH = 240;
+export const HEADER_MOBILE_HEIGHT = 52;
+export const HEADER_DESKTOP_HEIGHT = 60;
export const MenuIconButton = styled(IconButton)(({ theme }) => ({
paddingRight: '8px',
@@ -43,9 +43,13 @@ export const StyledRoot = styled(AppBar, {
}));
export const StyledToolbar = styled(Toolbar)(({ theme }) => ({
- minHeight: HEADER_MOBILE,
+ height: HEADER_MOBILE_HEIGHT,
+ padding: theme.spacing(0, 1),
+ [theme.breakpoints.up('sm')]: {
+ padding: theme.spacing(0, 2)
+ },
[theme.breakpoints.up('lg')]: {
- minHeight: HEADER_DESKTOP,
+ minHeight: HEADER_DESKTOP_HEIGHT,
padding: theme.spacing(0, 4)
}
}));
diff --git a/apps/smart-forms-app/src/components/Header/HeaderIcons.tsx b/apps/smart-forms-app/src/components/Header/HeaderIcons.tsx
new file mode 100644
index 000000000..044369956
--- /dev/null
+++ b/apps/smart-forms-app/src/components/Header/HeaderIcons.tsx
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DesktopHeaderIcons from './DesktopHeaderIcons.tsx';
+import MobileHeaderIcons from './MobileHeaderIcons.tsx';
+import { Stack } from '@mui/material';
+import { useTheme } from '@mui/material/styles';
+import useResponsive from '../../hooks/useResponsive.ts';
+
+function HeaderIcons() {
+ const theme = useTheme();
+ const isDesktop = useResponsive('up', 'lg');
+
+ return (
+
+ {isDesktop ? : }
+
+ );
+}
+
+export default HeaderIcons;
diff --git a/apps/smart-forms-app/src/components/Header/MobileHeaderWithoutQuestionnaire.tsx b/apps/smart-forms-app/src/components/Header/MobileHeaderIcons.tsx
similarity index 70%
rename from apps/smart-forms-app/src/components/Header/MobileHeaderWithoutQuestionnaire.tsx
rename to apps/smart-forms-app/src/components/Header/MobileHeaderIcons.tsx
index 20f3aac5c..0000adb3a 100644
--- a/apps/smart-forms-app/src/components/Header/MobileHeaderWithoutQuestionnaire.tsx
+++ b/apps/smart-forms-app/src/components/Header/MobileHeaderIcons.tsx
@@ -15,42 +15,45 @@
* limitations under the License.
*/
-import { Box } from '@mui/material';
-import NavErrorAlert from '../Nav/NavErrorAlert.tsx';
-import PersonPopover from './Popovers/PersonPopover.tsx';
+import HeaderPopover from './Popovers/HeaderPopover.tsx';
import FaceIcon from '@mui/icons-material/Face';
import PatientPopoverMenu from './Popovers/PatientPopoverMenu.tsx';
import MedicalServicesIcon from '@mui/icons-material/MedicalServices';
import UserPopoverMenu from './Popovers/UserPopoverMenu.tsx';
import { useTheme } from '@mui/material/styles';
+import { Box } from '@mui/material';
+import NotLaunchedPopover from './Popovers/NotLaunchedPopover.tsx';
import useConfigStore from '../../stores/useConfigStore.ts';
-function MobileHeaderWithQuestionnaire() {
- const smartClient = useConfigStore((state) => state.smartClient);
-
+function MobileHeaderIcons() {
const theme = useTheme();
+ const smartClient = useConfigStore((state) => state.smartClient);
const isNotLaunched = !smartClient;
return (
<>
{isNotLaunched ? (
-
-
+
+
) : null}
- }
+ }
menuContent={}
/>
- }
+ displayIcon={
+
+ }
menuContent={}
/>
>
);
}
-export default MobileHeaderWithQuestionnaire;
+export default MobileHeaderIcons;
diff --git a/apps/smart-forms-app/src/components/Header/MobileHeaderWithQuestionnaire.tsx b/apps/smart-forms-app/src/components/Header/MobileHeaderWithQuestionnaire.tsx
deleted file mode 100644
index 971c08138..000000000
--- a/apps/smart-forms-app/src/components/Header/MobileHeaderWithQuestionnaire.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2023 Commonwealth Scientific and Industrial Research
- * Organisation (CSIRO) ABN 41 687 119 230.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Box } from '@mui/material';
-import NavErrorAlert from '../Nav/NavErrorAlert.tsx';
-import PersonPopover from './Popovers/PersonPopover.tsx';
-import AssignmentIcon from '@mui/icons-material/Assignment';
-import QuestionnairePopoverMenu from './Popovers/QuestionnairePopoverMenu.tsx';
-import FaceIcon from '@mui/icons-material/Face';
-import PatientPopoverMenu from './Popovers/PatientPopoverMenu.tsx';
-import MedicalServicesIcon from '@mui/icons-material/MedicalServices';
-import UserPopoverMenu from './Popovers/UserPopoverMenu.tsx';
-import { useTheme } from '@mui/material/styles';
-import useConfigStore from '../../stores/useConfigStore.ts';
-
-function MobileHeaderWithQuestionnaire() {
- const smartClient = useConfigStore((state) => state.smartClient);
-
- const theme = useTheme();
-
- const isNotLaunched = !smartClient;
-
- return (
- <>
- {isNotLaunched ? (
-
-
-
- ) : null}
- }
- menuContent={}
- />
- }
- menuContent={}
- />
- }
- menuContent={}
- />
- >
- );
-}
-
-export default MobileHeaderWithQuestionnaire;
diff --git a/apps/smart-forms-app/src/components/Header/Popovers/HeaderPopover.tsx b/apps/smart-forms-app/src/components/Header/Popovers/HeaderPopover.tsx
new file mode 100644
index 000000000..9089868da
--- /dev/null
+++ b/apps/smart-forms-app/src/components/Header/Popovers/HeaderPopover.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import PopupState, { bindPopover, bindTrigger } from 'material-ui-popup-state';
+import { Avatar, IconButton, Popover, Stack, Typography } from '@mui/material';
+import type { ReactElement } from 'react';
+
+interface HeaderPopoverProps {
+ entity: 'Patient' | 'User' | 'Form';
+ bgColor: string;
+ displayIcon: ReactElement;
+ menuContent: ReactElement;
+}
+
+function HeaderPopover(props: HeaderPopoverProps) {
+ const { entity, bgColor, displayIcon, menuContent } = props;
+
+ return (
+ <>
+
+ {(popupState) => (
+
+
+
+ {displayIcon}
+
+
+ {entity}
+
+
+
+ {menuContent}
+
+
+ )}
+
+ >
+ );
+}
+
+export default HeaderPopover;
diff --git a/apps/smart-forms-app/src/components/Header/Popovers/PersonPopover.tsx b/apps/smart-forms-app/src/components/Header/Popovers/NotLaunchedPopover.tsx
similarity index 63%
rename from apps/smart-forms-app/src/components/Header/Popovers/PersonPopover.tsx
rename to apps/smart-forms-app/src/components/Header/Popovers/NotLaunchedPopover.tsx
index 4f1f326ca..a6ae7e08e 100644
--- a/apps/smart-forms-app/src/components/Header/Popovers/PersonPopover.tsx
+++ b/apps/smart-forms-app/src/components/Header/Popovers/NotLaunchedPopover.tsx
@@ -16,29 +16,25 @@
*/
import PopupState, { bindPopover, bindTrigger } from 'material-ui-popup-state';
-import { Avatar, IconButton, Popover } from '@mui/material';
-
-interface PersonPopoverProps {
- bgColor: string;
- displayIcon: JSX.Element;
- menuContent: JSX.Element;
-}
-
-function PersonPopover(props: PersonPopoverProps) {
- const { bgColor, displayIcon, menuContent } = props;
+import { IconButton, Popover, Stack, Typography } from '@mui/material';
+import ErrorIcon from '@mui/icons-material/Error';
+import { PopoverMenuWrapper } from './Popover.styles.ts';
+function NotLaunchedPopover() {
return (
<>
{(popupState) => (
-
- {displayIcon}
-
+
+
+
+
+
- {menuContent}
+
+
+ Save operations disabled, app not launched via SMART
+
+
)}
@@ -65,4 +59,4 @@ function PersonPopover(props: PersonPopoverProps) {
);
}
-export default PersonPopover;
+export default NotLaunchedPopover;
diff --git a/apps/smart-forms-app/src/components/Layout/Layout.styles.ts b/apps/smart-forms-app/src/components/Layout/Layout.styles.ts
index 2f9948f06..84b63b84b 100644
--- a/apps/smart-forms-app/src/components/Layout/Layout.styles.ts
+++ b/apps/smart-forms-app/src/components/Layout/Layout.styles.ts
@@ -16,9 +16,7 @@
*/
import { Box, styled } from '@mui/material';
-
-const APP_BAR_MOBILE = 64;
-const APP_BAR_DESKTOP = 72;
+import { HEADER_DESKTOP_HEIGHT, HEADER_MOBILE_HEIGHT } from '../Header/Header.styles.ts';
export const StyledRoot = styled(Box)({
display: 'flex',
@@ -30,15 +28,15 @@ export const Main = styled(Box)(({ theme }) => ({
flexGrow: 1,
overflow: 'auto',
minHeight: '100%',
- paddingTop: APP_BAR_MOBILE + 12,
- paddingBottom: theme.spacing(4),
+ paddingTop: HEADER_MOBILE_HEIGHT + 8,
+ paddingBottom: theme.spacing(3),
[theme.breakpoints.up('md')]: {
- paddingTop: APP_BAR_DESKTOP + 16,
+ paddingTop: HEADER_DESKTOP_HEIGHT + 12,
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1)
},
[theme.breakpoints.up('lg')]: {
- paddingTop: APP_BAR_DESKTOP + 16,
+ paddingTop: HEADER_DESKTOP_HEIGHT + 16,
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2)
}
diff --git a/apps/smart-forms-app/src/components/Logos/Logo.tsx b/apps/smart-forms-app/src/components/Logos/Logo.tsx
index 05b14d045..36680a05b 100644
--- a/apps/smart-forms-app/src/components/Logos/Logo.tsx
+++ b/apps/smart-forms-app/src/components/Logos/Logo.tsx
@@ -18,12 +18,21 @@
import { memo } from 'react';
import { Box, Typography } from '@mui/material';
import AppLogo from '../../data/images/logo.svg';
+import useResponsive from '../../hooks/useResponsive.ts';
+
+interface LogoProps {
+ isNav?: boolean;
+}
+
+const Logo = memo(function Logo(props: LogoProps) {
+ const { isNav } = props;
+
+ const isDesktop = useResponsive('up', 'lg');
-const Logo = memo(function Logo() {
return (
-
- Smart Forms
+
+ {isDesktop || isNav ? Smart Forms : null}
);
});
diff --git a/apps/smart-forms-app/src/components/Nav/Nav.styles.ts b/apps/smart-forms-app/src/components/Nav/Nav.styles.ts
index 978490285..bb8aa2ff1 100644
--- a/apps/smart-forms-app/src/components/Nav/Nav.styles.ts
+++ b/apps/smart-forms-app/src/components/Nav/Nav.styles.ts
@@ -64,12 +64,15 @@ export const StyledAlert = styled(Box, {
})<{ color: 'info' | 'error' }>(({ theme, color }) => ({
display: 'flex',
alignItems: 'center',
- padding: theme.spacing(1.5),
+ padding: theme.spacing(1.25),
borderRadius: Number(theme.shape.borderRadius) * 1.5,
backgroundColor: alpha(
color === 'error' ? theme.palette.error.light : theme.palette.info.light,
0.12
),
+ [theme.breakpoints.up('sm')]: {
+ padding: theme.spacing(1.5)
+ },
[theme.breakpoints.up('md')]: {
padding: theme.spacing(2)
}
diff --git a/apps/smart-forms-app/src/components/Nav/NavPatientDetails.tsx b/apps/smart-forms-app/src/components/Nav/NavPatientDetails.tsx
index 99d6ae78b..ead43d796 100644
--- a/apps/smart-forms-app/src/components/Nav/NavPatientDetails.tsx
+++ b/apps/smart-forms-app/src/components/Nav/NavPatientDetails.tsx
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Box } from '@mui/material';
+import { Box, Typography } from '@mui/material';
import FaceIcon from '@mui/icons-material/Face';
import { constructName } from '../../features/smartAppLaunch/utils/launchContext.ts';
import dayjs from 'dayjs';
@@ -37,6 +37,9 @@ function NavPatientDetails() {
+
+ Patient
+
{patient ? (
<>
diff --git a/apps/smart-forms-app/src/components/Toggles/QuestionnaireSourceToggle.tsx b/apps/smart-forms-app/src/components/Toggles/QuestionnaireSourceToggle.tsx
deleted file mode 100644
index c17e72598..000000000
--- a/apps/smart-forms-app/src/components/Toggles/QuestionnaireSourceToggle.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2023 Commonwealth Scientific and Industrial Research
- * Organisation (CSIRO) ABN 41 687 119 230.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { FormControlLabel, Switch, Typography } from '@mui/material';
-import { useContext } from 'react';
-import { SelectedQuestionnaireContext } from '../../features/dashboard/contexts/SelectedQuestionnaireContext.tsx';
-import useConfigStore from '../../stores/useConfigStore.ts';
-
-interface Props {
- setPage: (page: number) => void;
-}
-function QuestionnaireSourceToggle(props: Props) {
- const { setPage } = props;
-
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
- const updateQuestionnaireSource = useConfigStore((state) => state.updateQuestionnaireSource);
-
- const { clearSelectedQuestionnaire } = useContext(SelectedQuestionnaireContext);
-
- return (
- {
- updateQuestionnaireSource(questionnaireSource === 'local' ? 'remote' : 'local');
- clearSelectedQuestionnaire();
- setPage(0);
- }}
- />
- }
- label={
-
- {questionnaireSource}
-
- }
- />
- );
-}
-
-export default QuestionnaireSourceToggle;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNav.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNav.tsx
index d930e1ae8..711f17984 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNav.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNav.tsx
@@ -27,15 +27,14 @@ import GoToPlaygroundButton from '../DashboardPages/QuestionnairePage/Buttons/Go
import { CsiroLogoWrapper, NavLogoWrapper } from '../../../../components/Logos/Logo.styles.ts';
import { NavMiddleWrapper } from '../../../../components/Nav/Nav.styles.ts';
import useConfigStore from '../../../../stores/useConfigStore.ts';
+import { NAV_WIDTH } from '../../../../components/Header/Header.styles.ts';
-const NAV_WIDTH = 240;
-
-interface Props {
+interface DashboardNavProps {
openNav: boolean;
onCloseNav: () => void;
}
-export default function DashboardNav(props: Props) {
+export default function DashboardNav(props: DashboardNavProps) {
const { openNav, onCloseNav } = props;
const smartClient = useConfigStore((state) => state.smartClient);
@@ -52,12 +51,12 @@ export default function DashboardNav(props: Props) {
'& .simplebar-content': { height: 1, display: 'flex', flexDirection: 'column' }
}}>
-
+
-
+
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavItem.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavItem.tsx
index 22227ebeb..d670e97f2 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavItem.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavItem.tsx
@@ -17,13 +17,21 @@
import { ListItemButton, ListItemText, useTheme } from '@mui/material';
import { NavLink } from 'react-router-dom';
-import type { NavItem } from '../../../../types/Nav.interface.ts';
import { StyledNavItemIcon } from '../../../../components/Nav/Nav.styles.ts';
+import type { ReactNode } from 'react';
import { useContext } from 'react';
import { SelectedQuestionnaireContext } from '../../contexts/SelectedQuestionnaireContext.tsx';
-function DashboardNavItem(props: NavItem) {
- const { title, path, icon, disabled } = props;
+export interface DashboardNavItemProps {
+ title: string;
+ path: string;
+ icon: ReactNode;
+ disabled?: boolean;
+ onCloseNav: () => void;
+}
+
+function DashboardNavItem(props: DashboardNavItemProps) {
+ const { title, path, icon, disabled, onCloseNav } = props;
const { setSelectedQuestionnaire } = useContext(SelectedQuestionnaireContext);
const theme = useTheme();
@@ -35,7 +43,10 @@ function DashboardNavItem(props: NavItem) {
disableGutters
disabled={disabled}
data-test="list-button-dashboard-nav-page"
- onClick={() => setSelectedQuestionnaire(null)}
+ onClick={() => {
+ setSelectedQuestionnaire(null);
+ onCloseNav();
+ }}
sx={{
...theme.typography.subtitle2,
height: 48,
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavSection.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavSection.tsx
index 1992c2122..b115db3b0 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavSection.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardNav/DashboardNavSection.tsx
@@ -27,7 +27,13 @@ import {
} from '../../../../components/Nav/Nav.styles.ts';
import useConfigStore from '../../../../stores/useConfigStore.ts';
-const DashboardNavSection = memo(function DashboardNavSection() {
+interface DashboardNavSectionProps {
+ onCloseNav: () => void;
+}
+
+const DashboardNavSection = memo(function DashboardNavSection(props: DashboardNavSectionProps) {
+ const { onCloseNav } = props;
+
const smartClient = useConfigStore((state) => state.smartClient);
return (
@@ -40,12 +46,14 @@ const DashboardNavSection = memo(function DashboardNavSection() {
title={'Questionnaires'}
path={'/dashboard/questionnaires'}
icon={}
+ onCloseNav={onCloseNav}
/>
}
disabled={!smartClient}
+ onCloseNav={onCloseNav}
/>
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardHeading.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardHeading.tsx
deleted file mode 100644
index 4e50cbbf9..000000000
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardHeading.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2023 Commonwealth Scientific and Industrial Research
- * Organisation (CSIRO) ABN 41 687 119 230.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import { Box, Stack, Typography } from '@mui/material';
-import QuestionnaireSourceToggle from '../../../../components/Toggles/QuestionnaireSourceToggle.tsx';
-import useConfigStore from '../../../../stores/useConfigStore.ts';
-
-interface DashboardHeadingProps {
- headingText: string;
- setPage: (page: number) => void;
-}
-function DashboardHeading(props: DashboardHeadingProps) {
- const { headingText, setPage } = props;
-
- const debugMode = useConfigStore((state) => state.debugMode);
-
- return (
-
- {headingText}
-
- {debugMode ? : null}
-
- );
-}
-
-export default DashboardHeading;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTableHead.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTableHead.tsx
index 38413c1c1..7d1951e5b 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTableHead.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTableHead.tsx
@@ -15,36 +15,35 @@
* limitations under the License.
*/
-import type { TableAttributes } from '../../../renderer/types/table.interface.ts';
import { TableCell, TableHead, TableRow, TableSortLabel } from '@mui/material';
+import type { Header, SortDirection } from '@tanstack/react-table';
interface DashboardTableHeadProps {
- order: 'asc' | 'desc';
- orderBy: string;
- headLabel: TableAttributes[];
- onSort: (event: MouseEvent, property: keyof T) => void;
+ headers: Header[];
}
function DashboardTableHead(props: DashboardTableHeadProps) {
- const { order, orderBy, headLabel, onSort } = props;
+ const { headers } = props;
return (
- {headLabel.map((headCell) => (
-
- onSort(event as unknown as MouseEvent, headCell.id as keyof T)}>
- {headCell.label}
-
-
- ))}
+ {headers.map((header) => {
+ const label = (header.column.columnDef.header ?? '') as string;
+ const sortValue = header.column.getIsSorted() as SortDirection | false;
+
+ return (
+
+
+ {label}
+
+
+ );
+ })}
);
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTablePagination.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTablePagination.tsx
index f12342771..641cd7cb2 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTablePagination.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/DashboardTablePagination.tsx
@@ -15,37 +15,34 @@
* limitations under the License.
*/
-import { Box, Fade, TablePagination, Typography } from '@mui/material';
+import { Box, TablePagination } from '@mui/material';
+import type { Table } from '@tanstack/react-table';
+import type { ReactNode } from 'react';
-interface TablePaginationProps {
- isFetching: boolean;
- numOfItems: number;
- page: number;
- rowsPerPage: number;
- setPage: (page: number) => void;
- setRowsPerPage: (rowsPerPage: number) => void;
+interface DashboardTablePaginationProps {
+ table: Table;
+ children: ReactNode;
}
-function DashboardTablePagination(props: TablePaginationProps) {
- const { isFetching, numOfItems, page, rowsPerPage, setPage, setRowsPerPage } = props;
+function DashboardTablePagination(props: DashboardTablePaginationProps) {
+ const { table, children } = props;
+
+ const { pageSize, pageIndex } = table.getState().pagination;
+ const totalNumberOfItems = table.getRowModel().rows.length;
return (
-
-
- Updating...
-
-
+ {children}
setPage(newPage)}
+ count={table.getFilteredRowModel().rows.length}
+ rowsPerPage={pageSize}
+ page={pageIndex}
+ onPageChange={(_, page) => table.setPageIndex(page)}
onRowsPerPageChange={(event) => {
- setRowsPerPage(parseInt(event.target.value));
- setPage(0);
+ const size = event.target.value ? Number(event.target.value) : 10;
+ table.setPageSize(size);
}}
/>
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/PageHeading.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/PageHeading.tsx
new file mode 100644
index 000000000..7896c3f4c
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/PageHeading.tsx
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Box, Typography } from '@mui/material';
+import type { ReactNode } from 'react';
+
+interface PageHeadingProps {
+ children: ReactNode;
+}
+
+function PageHeading(props: PageHeadingProps) {
+ const { children } = props;
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default PageHeading;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx
index b175519c9..44fd5dedd 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx
@@ -15,28 +15,24 @@
* limitations under the License.
*/
-import { useState } from 'react';
-import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
+import { useContext, useState } from 'react';
import { createQuestionnaireResponse } from '../../../../../renderer/utils/qrItem.ts';
import { useNavigate } from 'react-router-dom';
import { postQuestionnaireToSMARTHealthIT } from '../../../../../save/api/saveQr.ts';
-import type { SelectedQuestionnaire } from '../../../../types/list.interface.ts';
import useQuestionnaireStore from '../../../../../../stores/useQuestionnaireStore.ts';
import useQuestionnaireResponseStore from '../../../../../../stores/useQuestionnaireResponseStore.ts';
import useConfigStore from '../../../../../../stores/useConfigStore.ts';
-import { LoadingButton } from '@mui/lab';
-
-interface Props {
- selectedQuestionnaire: SelectedQuestionnaire | null;
-}
-
-function CreateNewResponseButton(props: Props) {
- const { selectedQuestionnaire } = props;
+import { CircularProgress, IconButton, Stack, Typography } from '@mui/material';
+import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuestionnaireContext.tsx';
+import EditNoteIcon from '@mui/icons-material/EditNote';
+function CreateNewResponseButton() {
const smartClient = useConfigStore((state) => state.smartClient);
const buildSourceQuestionnaire = useQuestionnaireStore((state) => state.buildSourceQuestionnaire);
const buildSourceResponse = useQuestionnaireResponseStore((state) => state.buildSourceResponse);
+ const { selectedQuestionnaire } = useContext(SelectedQuestionnaireContext);
+
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
@@ -65,24 +61,30 @@ function CreateNewResponseButton(props: Props) {
setIsLoading(false);
}
+ const buttonIsDisabled = !selectedQuestionnaire?.listItem || isLoading;
+
return (
- }
- sx={{
- px: 2.5,
- backgroundColor: 'secondary.main',
- '&:hover': {
- backgroundColor: 'secondary.dark'
- }
- }}
- data-test="button-create-response"
- onClick={handleClick}>
- Create response
-
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+ Create response
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/GoToPlaygroundButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/GoToPlaygroundButton.tsx
index 39097ee73..99a3bdf07 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/GoToPlaygroundButton.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/GoToPlaygroundButton.tsx
@@ -16,7 +16,6 @@
*/
import { Button } from '@mui/material';
-import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
import { useNavigate } from 'react-router-dom';
function GoToPlaygroundButton() {
@@ -26,7 +25,6 @@ function GoToPlaygroundButton() {
}
sx={{ borderRadius: 15 }}
onClick={() => {
navigate('/playground');
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/ViewExistingResponsesButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/ViewExistingResponsesButton.tsx
index 534ff6fbb..33a3019b7 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/ViewExistingResponsesButton.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/ViewExistingResponsesButton.tsx
@@ -16,7 +16,6 @@
*/
import { useContext, useMemo } from 'react';
-import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
import { getClientBundlePromise, getResponsesFromBundle } from '../../../../utils/dashboard.ts';
import { useQuery } from '@tanstack/react-query';
import type { Bundle, QuestionnaireResponse } from 'fhir/r4';
@@ -24,15 +23,15 @@ import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuest
import { useNavigate } from 'react-router-dom';
import { useSnackbar } from 'notistack';
import useConfigStore from '../../../../../../stores/useConfigStore.ts';
-import { CircularProgress } from '@mui/material';
-import { LoadingButton } from '@mui/lab';
+import { CircularProgress, IconButton, Stack, Typography } from '@mui/material';
+import HighlightOffIcon from '@mui/icons-material/HighlightOff';
+import GradingIcon from '@mui/icons-material/Grading';
function ViewExistingResponsesButton() {
const { selectedQuestionnaire, setExistingResponses } = useContext(SelectedQuestionnaireContext);
const smartClient = useConfigStore((state) => state.smartClient);
const patient = useConfigStore((state) => state.patient);
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
@@ -63,7 +62,6 @@ function ViewExistingResponsesButton() {
!!selectedQuestionnaire &&
questionnaireRefParam !== '' &&
patientIdParam !== '' &&
- questionnaireSource === 'remote' &&
!!smartClient
}
);
@@ -86,29 +84,37 @@ function ViewExistingResponsesButton() {
navigate('/dashboard/responses');
}
+ const buttonIsDisabled = !selectedQuestionnaire || existingResponses.length === 0 || isFetching;
+
return (
-
- ) : data && existingResponses.length === 0 ? null : (
-
- )
- }
- data-test="button-view-responses"
- sx={{ width: 175 }}
- onClick={handleClick}>
- {data && existingResponses.length === 0 && !isFetching
- ? 'No Responses Found'
- : 'View Responses'}
-
+
+
+ {isFetching && selectedQuestionnaire ? (
+
+ ) : data && existingResponses.length === 0 ? (
+
+ ) : (
+
+ )}
+
+
+
+ {isFetching && selectedQuestionnaire
+ ? 'Loading responses'
+ : data && existingResponses.length === 0
+ ? 'No responses found'
+ : 'View responses'}
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTable.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTable.tsx
new file mode 100644
index 000000000..41f0eb2ea
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTable.tsx
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext, useMemo, useState } from 'react';
+import { SelectedQuestionnaireContext } from '../../../contexts/SelectedQuestionnaireContext.tsx';
+import type { SortingState } from '@tanstack/react-table';
+import {
+ getCoreRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable
+} from '@tanstack/react-table';
+import useDebounce from '../../../../renderer/hooks/useDebounce.ts';
+import useFetchQuestionnaires from '../../../hooks/useFetchQuestionnaires.ts';
+import { createQuestionnaireTableColumns } from '../../../utils/tableColumns.ts';
+import type { Questionnaire } from 'fhir/r4';
+import QuestionnaireTableView from './QuestionnaireTableView.tsx';
+
+function QuestionnaireTable() {
+ const { selectedQuestionnaire, setSelectedQuestionnaire } = useContext(
+ SelectedQuestionnaireContext
+ );
+
+ // search questionnaires
+ const [searchInput, setSearchInput] = useState('');
+ const debouncedInput = useDebounce(searchInput, 300);
+
+ const {
+ remoteQuestionnaires,
+ questionnaireListItems,
+ fetchStatus,
+ fetchError,
+ isInitialLoading,
+ isFetching
+ } = useFetchQuestionnaires(searchInput, debouncedInput);
+
+ const columns = useMemo(() => createQuestionnaireTableColumns(), []);
+
+ const [sorting, setSorting] = useState([
+ {
+ id: 'date',
+ desc: true
+ }
+ ]);
+
+ const table = useReactTable({
+ data: questionnaireListItems,
+ columns: columns,
+ onSortingChange: setSorting,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ state: {
+ sorting
+ }
+ });
+
+ function handleRowClick(id: string) {
+ const selectedItem = questionnaireListItems.find((item) => item.id === id);
+
+ if (selectedItem) {
+ if (selectedItem.id === selectedQuestionnaire?.listItem.id) {
+ setSelectedQuestionnaire(null);
+ } else {
+ const resource = remoteQuestionnaires?.entry?.find(
+ (entry) => entry.resource?.id === id
+ )?.resource;
+
+ if (resource) {
+ setSelectedQuestionnaire({
+ listItem: selectedItem,
+ resource: resource as Questionnaire
+ });
+ } else {
+ setSelectedQuestionnaire(null);
+ }
+ }
+ }
+ }
+
+ return (
+ {
+ table.setPageIndex(0);
+ setSearchInput(input);
+ }}
+ onRowClick={handleRowClick}
+ onSelectQuestionnaire={setSelectedQuestionnaire}
+ />
+ );
+}
+
+export default QuestionnaireTable;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx
new file mode 100644
index 000000000..9d92ca720
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QuestionnaireListToolbar from './TableComponents/QuestionnaireListToolbar.tsx';
+import Scrollbar from '../../../../../components/Scrollbar/Scrollbar.tsx';
+import { Fade, Table as MuiTable, TableBody, TableContainer, Typography } from '@mui/material';
+import DashboardTableHead from '../DashboardTableHead.tsx';
+import QuestionnaireTableRow from './TableComponents/QuestionnaireTableRow.tsx';
+import QuestionnaireListFeedback from './TableComponents/QuestionnaireListFeedback.tsx';
+import DashboardTablePagination from '../DashboardTablePagination.tsx';
+import type { Table } from '@tanstack/react-table';
+import type {
+ QuestionnaireListItem,
+ SelectedQuestionnaire
+} from '../../../types/list.interface.ts';
+
+interface QuestionnaireTableViewProps {
+ table: Table;
+ searchInput: string;
+ debouncedInput: string;
+ fetchStatus: 'error' | 'success' | 'loading';
+ isInitialLoading: boolean;
+ isFetching: boolean;
+ fetchError: unknown;
+ selectedQuestionnaire: SelectedQuestionnaire | null;
+ onSearch: (input: string) => void;
+ onRowClick: (id: string) => void;
+ onSelectQuestionnaire: (selected: SelectedQuestionnaire | null) => void;
+}
+
+function QuestionnaireTableView(props: QuestionnaireTableViewProps) {
+ const {
+ table,
+ searchInput,
+ debouncedInput,
+ fetchStatus,
+ isInitialLoading,
+ isFetching,
+ fetchError,
+ selectedQuestionnaire,
+ onSearch,
+ onRowClick,
+ onSelectQuestionnaire
+ } = props;
+
+ const headers = table.getHeaderGroups()[0].headers;
+
+ const isEmpty =
+ table.getRowModel().rows.length === 0 && !!debouncedInput && fetchStatus !== 'loading';
+
+ return (
+ <>
+ onSelectQuestionnaire(null)}
+ onSearch={onSearch}
+ />
+
+
+
+
+
+
+ {table.getRowModel().rows.map((row) => {
+ const rowData = row.original;
+ const isSelected = selectedQuestionnaire?.listItem.id === rowData.id;
+
+ return (
+ onRowClick(rowData.id)}
+ />
+ );
+ })}
+
+
+ {isEmpty || fetchStatus === 'error' || isInitialLoading ? (
+
+ ) : null}
+
+
+
+
+
+
+
+ Updating...
+
+
+
+ >
+ );
+}
+
+export default QuestionnaireTableView;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnairesPage.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnairesPage.tsx
index bb80ce548..19e7cab31 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnairesPage.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnairesPage.tsx
@@ -15,115 +15,12 @@
* limitations under the License.
*/
-import { useContext, useMemo, useRef, useState } from 'react';
-import { Card, Container, Fade, Stack, Table, TableBody, TableContainer } from '@mui/material';
-import Scrollbar from '../../../../../components/Scrollbar/Scrollbar.tsx';
-import QuestionnaireListToolbar from './TableComponents/QuestionnaireListToolbar.tsx';
-import { applySortFilter, getComparator } from '../../../utils/dashboard.ts';
-import type { Questionnaire } from 'fhir/r4';
-import useDebounce from '../../../../renderer/hooks/useDebounce.ts';
-import QuestionnaireListFeedback from './TableComponents/QuestionnaireListFeedback.tsx';
-import CreateNewResponseButton from './Buttons/CreateNewResponseButton.tsx';
-import ViewExistingResponsesButton from './Buttons/ViewExistingResponsesButton.tsx';
-import { SelectedQuestionnaireContext } from '../../../contexts/SelectedQuestionnaireContext.tsx';
+import { Card, Container, Fade } from '@mui/material';
import { Helmet } from 'react-helmet';
-import type { TableAttributes } from '../../../../renderer/types/table.interface.ts';
-import type { QuestionnaireListItem } from '../../../types/list.interface.ts';
-import useConfigStore from '../../../../../stores/useConfigStore.ts';
-import DashboardHeading from '../DashboardHeading.tsx';
-import QuestionnaireTableRow from './TableComponents/QuestionnaireTableRow.tsx';
-import DashboardTablePagination from '../DashboardTablePagination.tsx';
-import useFetchQuestionnaires from '../../../hooks/useFetchQuestionnaires.ts';
-import DashboardTableHead from '../DashboardTableHead.tsx';
-
-const tableHeaders: TableAttributes[] = [
- { id: 'title', label: 'Title', alignRight: false },
- { id: 'publisher', label: 'Publisher', alignRight: false },
- { id: 'date', label: 'Date', alignRight: false },
- { id: 'status', label: 'Status', alignRight: false }
-];
+import PageHeading from '../PageHeading.tsx';
+import QuestionnaireTable from './QuestionnaireTable.tsx';
function QuestionnairesPage() {
- const smartClient = useConfigStore((state) => state.smartClient);
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
-
- const { selectedQuestionnaire, setSelectedQuestionnaire } = useContext(
- SelectedQuestionnaireContext
- );
-
- // Scroll to buttons row when questionnaire is selected - for screens with small height
- const buttonsRef = useRef(null);
- function executeScroll() {
- if (buttonsRef.current) {
- buttonsRef.current.scrollIntoView({ behavior: 'smooth' });
- }
- }
-
- const [page, setPage] = useState(0);
- const [rowsPerPage, setRowsPerPage] = useState(10);
-
- const [order, setOrder] = useState<'asc' | 'desc'>('desc');
- const [orderBy, setOrderBy] = useState('date');
-
- // search questionnaires
- const [searchInput, setSearchInput] = useState('');
- const debouncedInput = useDebounce(searchInput, 300);
-
- const {
- remoteQuestionnaires,
- localQuestionnaires,
- questionnaireListItems,
- fetchStatus,
- fetchError,
- isInitialLoading,
- isFetching
- } = useFetchQuestionnaires(searchInput, debouncedInput, questionnaireSource);
-
- // sort or perform client-side filtering or items
- const filteredListItems: QuestionnaireListItem[] = useMemo(
- () =>
- applySortFilter(
- questionnaireListItems,
- getComparator(order, orderBy, 'questionnaire'),
- questionnaireSource,
- debouncedInput
- ) as QuestionnaireListItem[],
- [debouncedInput, order, orderBy, questionnaireListItems, questionnaireSource]
- );
-
- const isEmpty = filteredListItems.length === 0 && !!debouncedInput && fetchStatus !== 'loading';
-
- // Event handlers
- const handleSort = (_: MouseEvent, property: keyof QuestionnaireListItem) => {
- const isAsc = orderBy === property && order === 'asc';
- setOrder(isAsc ? 'desc' : 'asc');
- setOrderBy(property);
- };
-
- const handleRowClick = (id: string) => {
- const selectedItem = filteredListItems.find((item) => item.id === id);
-
- if (selectedItem) {
- executeScroll();
- if (selectedItem.id === selectedQuestionnaire?.listItem.id) {
- setSelectedQuestionnaire(null);
- } else {
- const bundle =
- questionnaireSource === 'remote' ? remoteQuestionnaires : localQuestionnaires;
- const resource = bundle?.entry?.find((entry) => entry.resource?.id === id)?.resource;
-
- if (resource) {
- setSelectedQuestionnaire({
- listItem: selectedItem,
- resource: resource as Questionnaire
- });
- } else {
- setSelectedQuestionnaire(null);
- }
- }
- }
- };
-
return (
<>
@@ -131,73 +28,11 @@ function QuestionnairesPage() {
-
+ Questionnaires
- setSelectedQuestionnaire(null)}
- onSearch={(input) => {
- setPage(0);
- setSearchInput(input);
- }}
- />
-
-
-
-
-
-
- {filteredListItems
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((row) => {
- const { id } = row;
- const isSelected = selectedQuestionnaire?.listItem.id === id;
-
- return (
- handleRowClick(id)}
- />
- );
- })}
-
-
- {isEmpty || fetchStatus === 'error' || isInitialLoading ? (
-
- ) : null}
-
-
-
-
-
+
-
-
- {smartClient ? : null}
-
-
>
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListFeedback.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListFeedback.tsx
index 0e2fcb34a..957475b2b 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListFeedback.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListFeedback.tsx
@@ -16,7 +16,6 @@
*/
import { Stack, TableBody, TableCell, TableRow } from '@mui/material';
-import useConfigStore from '../../../../../../stores/useConfigStore.ts';
import DashboardFeedbackMessage from '../../DashboardFeedbackMessage.tsx';
import { useSnackbar } from 'notistack';
@@ -30,14 +29,12 @@ interface Props {
function QuestionnaireListFeedback(props: Props) {
const { isEmpty, isInitialLoading, status, searchInput, error } = props;
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
-
const { enqueueSnackbar } = useSnackbar();
let feedbackType: 'error' | 'empty' | 'loading' | null = null;
if (status === 'error') {
feedbackType = 'error';
- } else if (isInitialLoading && questionnaireSource === 'remote') {
+ } else if (isInitialLoading) {
feedbackType = 'loading';
} else if (isEmpty) {
feedbackType = 'empty';
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.styles.ts b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.styles.ts
index 2673c8007..7cf5e7a41 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.styles.ts
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.styles.ts
@@ -18,7 +18,11 @@
import { alpha, styled } from '@mui/material/styles';
import { OutlinedInput, Toolbar } from '@mui/material';
import type { QuestionnaireResponse } from 'fhir/r4';
-import type { ResponseListItem, SelectedQuestionnaire } from '../../../../types/list.interface.ts';
+import type {
+ QuestionnaireListItem,
+ ResponseListItem,
+ SelectedQuestionnaire
+} from '../../../../types/list.interface.ts';
export const StyledRoot = styled(Toolbar)(({ theme }) => ({
height: 80,
@@ -44,7 +48,7 @@ export const StyledSearch = styled(OutlinedInput)(({ theme }) => ({
}
}));
-export function getToolBarColors(
+export function getResponseToolBarColors(
selected: ResponseListItem | undefined,
selectedQuestionnaire: SelectedQuestionnaire | null,
existingResponses: QuestionnaireResponse[]
@@ -63,3 +67,12 @@ export function getToolBarColors(
: null)
};
}
+
+export function getQuestionnaireToolBarColors(selected: QuestionnaireListItem | undefined) {
+ return {
+ ...(selected && {
+ color: 'primary.main',
+ bgcolor: 'pale.primary'
+ })
+ };
+}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.tsx
index 483e18571..5b1fdc866 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbar.tsx
@@ -15,38 +15,38 @@
* limitations under the License.
*/
-import { Box, IconButton, InputAdornment, Tooltip, Typography } from '@mui/material';
+import { Box, InputAdornment, Typography } from '@mui/material';
+
import type { ChangeEvent } from 'react';
import { useState } from 'react';
import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
-import { StyledRoot, StyledSearch } from './QuestionnaireListToolbar.styles.ts';
+import {
+ getQuestionnaireToolBarColors,
+ StyledRoot,
+ StyledSearch
+} from './QuestionnaireListToolbar.styles.ts';
import type { QuestionnaireListItem } from '../../../../types/list.interface.ts';
import { StyledAlert } from '../../../../../../components/Nav/Nav.styles.ts';
+import QuestionnaireListToolbarButtons from './QuestionnaireListToolbarButtons.tsx';
-interface Props {
+interface QuestionnaireListToolbarProps {
selected: QuestionnaireListItem | undefined;
searchInput: string;
- clearSelection: () => void;
+ onClearSelection: () => void;
onSearch: (searchInput: string) => void;
}
-function QuestionnaireListToolbar(props: Props) {
- const { selected, searchInput, clearSelection, onSearch } = props;
+function QuestionnaireListToolbar(props: QuestionnaireListToolbarProps) {
+ const { selected, searchInput, onClearSelection, onSearch } = props;
const [alertVisible, setAlertVisible] = useState(true);
+ const toolBarColors = getQuestionnaireToolBarColors(selected);
+
return (
-
+
{selected ? (
-
- {selected.title} selected
-
+ {selected.title} selected
) : (
) : null}
- {selected ? (
-
-
-
-
-
- ) : null}
+ {selected ? : null}
);
}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbarButtons.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbarButtons.tsx
new file mode 100644
index 000000000..5ef51d214
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireListToolbarButtons.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Box, IconButton, Stack, Typography } from '@mui/material';
+import CreateNewResponseButton from '../Buttons/CreateNewResponseButton.tsx';
+import ViewExistingResponsesButton from '../Buttons/ViewExistingResponsesButton.tsx';
+import ClearIcon from '@mui/icons-material/Clear';
+import useConfigStore from '../../../../../../stores/useConfigStore.ts';
+
+interface QuestionnaireListToolbarButtonsProps {
+ onClearSelection: () => void;
+}
+
+function QuestionnaireListToolbarButtons(props: QuestionnaireListToolbarButtonsProps) {
+ const { onClearSelection } = props;
+
+ const smartClient = useConfigStore((state) => state.smartClient);
+
+ return (
+
+
+
+ {smartClient ? : null}
+
+
+
+
+
+
+ Unselect
+
+
+
+ );
+}
+
+export default QuestionnaireListToolbarButtons;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTable.styles.ts b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTable.styles.ts
index 05829f78d..9b1eb7a05 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTable.styles.ts
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTable.styles.ts
@@ -21,7 +21,7 @@ export const StyledAvatar = styled(Avatar, {
shouldForwardProp: (prop) => prop !== 'avatarColor'
})<{ avatarColor: string }>(({ avatarColor }) => ({
backgroundColor: avatarColor,
- margin: '12px 4px 12px 10px',
+ margin: '10px 4px 10px 10px',
width: '36px',
height: '36px'
}));
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTableRow.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTableRow.tsx
index dcff2d67e..7ce3a7d36 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTableRow.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/TableComponents/QuestionnaireTableRow.tsx
@@ -46,20 +46,14 @@ function QuestionnaireTableRow(props: QuestionnaireTableRowProps) {
-
- {title}
-
+ {title}
-
- {publisher}
-
+ {publisher}
-
- {dayjs(date).format('LL')}
-
+ {date ? dayjs(date).format('LL') : '-'}
-
+
{status}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/BackToQuestionnairesButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/BackToQuestionnairesButton.tsx
index 79d051a65..062fd2975 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/BackToQuestionnairesButton.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/BackToQuestionnairesButton.tsx
@@ -15,33 +15,28 @@
* limitations under the License.
*/
-import { Button } from '@mui/material';
+import { IconButton, Tooltip } from '@mui/material';
import { useContext } from 'react';
-import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuestionnaireContext.tsx';
import { useNavigate } from 'react-router-dom';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
function BackToQuestionnairesButton() {
const { existingResponses } = useContext(SelectedQuestionnaireContext);
const navigate = useNavigate();
return existingResponses.length > 0 ? (
- }
- data-test="button-responses-go-back"
- sx={{
- px: 2.5,
- backgroundColor: 'primary.main',
- '&:hover': {
- backgroundColor: 'primary.dark'
- }
- }}
- onClick={() => {
- navigate('/dashboard/questionnaires');
- }}>
- Go back
-
+
+ {
+ navigate('/dashboard/questionnaires');
+ }}
+ sx={{ ml: -1, mr: 0.75 }}>
+
+
+
) : null;
}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx
index 4e4eafe5d..2216c2925 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx
@@ -16,7 +16,6 @@
*/
import { useMemo, useState } from 'react';
-import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
import { useQuery } from '@tanstack/react-query';
import type { Bundle, Questionnaire } from 'fhir/r4';
import {
@@ -31,7 +30,8 @@ import { assembleIfRequired } from '../../../../../assemble/utils/assemble.ts';
import useConfigStore from '../../../../../../stores/useConfigStore.ts';
import useQuestionnaireStore from '../../../../../../stores/useQuestionnaireStore.ts';
import useQuestionnaireResponseStore from '../../../../../../stores/useQuestionnaireResponseStore.ts';
-import { LoadingButton } from '@mui/lab';
+import { CircularProgress, IconButton, Stack, Typography } from '@mui/material';
+import OpenInNewIcon from '@mui/icons-material/OpenInNew';
interface Props {
selectedResponse: SelectedResponse | null;
@@ -125,24 +125,30 @@ function OpenResponseButton(props: Props) {
setIsLoading(false);
}
+ const buttonIsDisabled = !selectedResponse || !referencedQuestionnaire || isLoading;
+
return (
- }
- sx={{
- px: 2.25,
- backgroundColor: 'secondary.main',
- '&:hover': {
- backgroundColor: 'secondary.dark'
- }
- }}
- data-test="button-open-response"
- onClick={handleClick}>
- Open Response
-
+
+
+ {isLoading ? (
+
+ ) : (
+
+ )}
+
+
+
+ Open Response
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx
index ab6f5a699..49bd4a220 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx
@@ -15,101 +15,12 @@
* limitations under the License.
*/
-import { useMemo, useRef, useState } from 'react';
-import { Box, Card, Container, Fade, Stack, Table, TableBody, TableContainer } from '@mui/material';
-import { applySortFilter, getComparator } from '../../../utils/dashboard.ts';
-import type { QuestionnaireResponse } from 'fhir/r4';
-import ResponseListToolbar from './TableComponents/ResponseListToolbar.tsx';
-import ResponseListFeedback from './TableComponents/ResponseListFeedback.tsx';
-import BackToQuestionnairesButton from './Buttons/BackToQuestionnairesButton.tsx';
-import OpenResponseButton from './Buttons/OpenResponseButton.tsx';
-import useDebounce from '../../../../renderer/hooks/useDebounce.ts';
+import { Card, Container, Fade } from '@mui/material';
import { Helmet } from 'react-helmet';
-import type { TableAttributes } from '../../../../renderer/types/table.interface.ts';
-import type { ResponseListItem, SelectedResponse } from '../../../types/list.interface.ts';
-import useConfigStore from '../../../../../stores/useConfigStore.ts';
-import DashboardHeading from '../DashboardHeading.tsx';
-import ResponseTableRow from './TableComponents/ResponseTableRow.tsx';
-import DashboardTablePagination from '../DashboardTablePagination.tsx';
-import useFetchResponses from '../../../hooks/useFetchResponses.ts';
-import DashboardTableHead from '../DashboardTableHead.tsx';
-
-const tableHeaders: TableAttributes[] = [
- { id: 'title', label: 'Questionnaire Title', alignRight: false },
- { id: 'author', label: 'Author', alignRight: false },
- { id: 'authored', label: 'Authored On', alignRight: false },
- { id: 'status', label: 'Status', alignRight: false }
-];
+import PageHeading from '../PageHeading.tsx';
+import ResponsesTable from './ResponsesTable.tsx';
function ResponsesPage() {
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
-
- // Scroll to buttons row when response is selected - for screens with small height
- const buttonsRef = useRef(null);
- function executeScroll() {
- if (buttonsRef.current) {
- buttonsRef.current.scrollIntoView({ behavior: 'smooth' });
- }
- }
-
- const [page, setPage] = useState(0);
- const [rowsPerPage, setRowsPerPage] = useState(10);
-
- const [order, setOrder] = useState<'asc' | 'desc'>('desc');
- const [selectedResponse, setSelectedResponse] = useState(null);
- const [orderBy, setOrderBy] = useState('authored');
- const [searchInput, setSearchInput] = useState('');
-
- const debouncedInput = useDebounce(searchInput, 300);
-
- const { responses, responseListItems, fetchStatus, fetchError, isFetching } = useFetchResponses(
- searchInput,
- debouncedInput,
- questionnaireSource
- );
-
- // sort or perform client-side filtering or items
- const filteredListItems: ResponseListItem[] = useMemo(
- () =>
- applySortFilter(
- responseListItems,
- getComparator(order, orderBy, 'response'),
- questionnaireSource
- ) as ResponseListItem[],
- [order, orderBy, responseListItems, questionnaireSource]
- );
-
- const isEmpty = filteredListItems.length === 0 && fetchStatus !== 'loading';
-
- // Event handlers
- const handleSort = (_: MouseEvent, property: keyof ResponseListItem) => {
- const isAsc = orderBy === property && order === 'asc';
- setOrder(isAsc ? 'desc' : 'asc');
- setOrderBy(property);
- };
-
- const handleRowClick = (id: string) => {
- const selectedItem = filteredListItems.find((item) => item.id === id);
-
- if (selectedItem) {
- executeScroll();
- if (selectedItem.id === selectedResponse?.listItem.id) {
- setSelectedResponse(null);
- } else {
- const resource = responses?.entry?.find((entry) => entry.resource?.id === id)?.resource;
-
- if (resource) {
- setSelectedResponse({
- listItem: selectedItem,
- resource: resource as QuestionnaireResponse
- });
- } else {
- setSelectedResponse(null);
- }
- }
- }
- };
-
return (
<>
@@ -117,73 +28,11 @@ function ResponsesPage() {
-
+ Responses
- setSelectedResponse(null)}
- onSearch={(input) => {
- setPage(0);
- setSearchInput(input);
- }}
- />
-
-
-
-
-
- {filteredListItems
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((row) => {
- const { id } = row;
- const isSelected = selectedResponse?.listItem.id === id;
-
- return (
- handleRowClick(id)}
- />
- );
- })}
-
-
- {(isEmpty || fetchStatus === 'error' || fetchStatus === 'loading') &&
- filteredListItems.length === 0 ? (
-
- ) : null}
-
-
-
-
+
-
-
-
-
-
-
>
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTable.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTable.tsx
new file mode 100644
index 000000000..3d7d3c218
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTable.tsx
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useMemo, useState } from 'react';
+import type { SelectedResponse } from '../../../types/list.interface.ts';
+import useDebounce from '../../../../renderer/hooks/useDebounce.ts';
+import useFetchResponses from '../../../hooks/useFetchResponses.ts';
+import { createResponseTableColumns } from '../../../utils/tableColumns.ts';
+import type { SortingState } from '@tanstack/react-table';
+import {
+ getCoreRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable
+} from '@tanstack/react-table';
+import type { QuestionnaireResponse } from 'fhir/r4';
+import ResponsesTableView from './ResponsesTableView.tsx';
+
+function ResponsesTable() {
+ const [selectedResponse, setSelectedResponse] = useState(null);
+ const [searchInput, setSearchInput] = useState('');
+
+ const debouncedInput = useDebounce(searchInput, 300);
+
+ const { responses, responseListItems, fetchStatus, fetchError, isFetching } = useFetchResponses(
+ searchInput,
+ debouncedInput
+ );
+
+ const columns = useMemo(() => createResponseTableColumns(), []);
+
+ const [sorting, setSorting] = useState([
+ {
+ id: 'authored',
+ desc: true
+ }
+ ]);
+
+ const table = useReactTable({
+ data: responseListItems,
+ columns: columns,
+ onSortingChange: setSorting,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ state: {
+ sorting
+ }
+ });
+
+ function handleRowClick(id: string) {
+ const selectedItem = responseListItems.find((item) => item.id === id);
+
+ if (selectedItem) {
+ if (selectedItem.id === selectedResponse?.listItem.id) {
+ setSelectedResponse(null);
+ } else {
+ const resource = responses?.entry?.find((entry) => entry.resource?.id === id)?.resource;
+
+ if (resource) {
+ setSelectedResponse({
+ listItem: selectedItem,
+ resource: resource as QuestionnaireResponse
+ });
+ } else {
+ setSelectedResponse(null);
+ }
+ }
+ }
+ }
+
+ return (
+ {
+ table.setPageIndex(0);
+ setSearchInput(input);
+ }}
+ onRowClick={handleRowClick}
+ onSelectResponse={setSelectedResponse}
+ />
+ );
+}
+
+export default ResponsesTable;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTableView.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTableView.tsx
new file mode 100644
index 000000000..fd736be6c
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/ResponsesTableView.tsx
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ResponseListToolbar from './TableComponents/ResponseListToolbar.tsx';
+import { Fade, Table as MuiTable, TableBody, TableContainer, Typography } from '@mui/material';
+import DashboardTableHead from '../DashboardTableHead.tsx';
+import ResponseTableRow from './TableComponents/ResponseTableRow.tsx';
+import ResponseListFeedback from './TableComponents/ResponseListFeedback.tsx';
+import DashboardTablePagination from '../DashboardTablePagination.tsx';
+import type { ResponseListItem, SelectedResponse } from '../../../types/list.interface.ts';
+import type { Table } from '@tanstack/react-table';
+
+interface ResponsesTableViewProps {
+ table: Table;
+ searchInput: string;
+ debouncedInput: string;
+ fetchStatus: 'error' | 'success' | 'loading';
+ isFetching: boolean;
+ fetchError: unknown;
+ selectedResponse: SelectedResponse | null;
+ onSearch: (input: string) => void;
+ onRowClick: (id: string) => void;
+ onSelectResponse: (selected: SelectedResponse | null) => void;
+}
+
+function ResponsesTableView(props: ResponsesTableViewProps) {
+ const {
+ table,
+ searchInput,
+ debouncedInput,
+ fetchStatus,
+ isFetching,
+ fetchError,
+ selectedResponse,
+ onSearch,
+ onRowClick,
+ onSelectResponse
+ } = props;
+
+ const headers = table.getHeaderGroups()[0].headers;
+
+ const isEmpty =
+ table.getRowModel().rows.length === 0 && !!debouncedInput && fetchStatus !== 'loading';
+
+ return (
+ <>
+ onSelectResponse(null)}
+ onSearch={onSearch}
+ />
+
+
+
+
+
+ {table.getRowModel().rows.map((row) => {
+ const rowData = row.original;
+ const isSelected = selectedResponse?.listItem.id === rowData.id;
+
+ return (
+ onRowClick(rowData.id)}
+ />
+ );
+ })}
+
+
+ {(isEmpty || fetchStatus === 'error' || fetchStatus === 'loading') &&
+ table.getRowModel().rows.length === 0 ? (
+
+ ) : null}
+
+
+
+
+
+
+ Updating...
+
+
+
+ >
+ );
+}
+
+export default ResponsesTableView;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListFeedback.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListFeedback.tsx
index 5f3632321..cf8644142 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListFeedback.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListFeedback.tsx
@@ -16,7 +16,6 @@
*/
import { Stack, TableBody, TableCell, TableRow } from '@mui/material';
-import useConfigStore from '../../../../../../stores/useConfigStore.ts';
import DashboardFeedbackMessage from '../../DashboardFeedbackMessage.tsx';
import { useSnackbar } from 'notistack';
@@ -30,14 +29,12 @@ interface Props {
function ResponseListFeedback(props: Props) {
const { isEmpty, status, searchInput, error } = props;
- const questionnaireSource = useConfigStore((state) => state.questionnaireSource);
-
const { enqueueSnackbar } = useSnackbar();
let feedbackType: 'error' | 'empty' | 'loading' | null = null;
if (status === 'error') {
feedbackType = 'error';
- } else if (status === 'loading' && questionnaireSource === 'remote') {
+ } else if (status === 'loading') {
feedbackType = 'loading';
} else if (isEmpty) {
feedbackType = 'empty';
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbar.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbar.tsx
index 26b352e44..de5d3ba58 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbar.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbar.tsx
@@ -15,18 +15,10 @@
* limitations under the License.
*/
-import {
- Box,
- IconButton,
- InputAdornment,
- LinearProgress,
- Tooltip,
- Typography,
- useTheme
-} from '@mui/material';
+import { Box, InputAdornment, LinearProgress, Typography } from '@mui/material';
import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
import {
- getToolBarColors,
+ getResponseToolBarColors,
StyledRoot,
StyledSearch
} from '../../QuestionnairePage/TableComponents/QuestionnaireListToolbar.styles.ts';
@@ -34,31 +26,36 @@ import dayjs from 'dayjs';
import type { ChangeEvent } from 'react';
import { useContext } from 'react';
import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuestionnaireContext.tsx';
-import { constructName } from '../../../../../smartAppLaunch/utils/launchContext.ts';
-import type { ResponseListItem } from '../../../../types/list.interface.ts';
-import useConfigStore from '../../../../../../stores/useConfigStore.ts';
+import type { SelectedResponse } from '../../../../types/list.interface.ts';
+import BackToQuestionnairesButton from '../Buttons/BackToQuestionnairesButton.tsx';
+import ResponseListToolbarButtons from './ResponseListToolbarButtons.tsx';
+import useResponsive from '../../../../../../hooks/useResponsive.ts';
-interface Props {
- selected: ResponseListItem | undefined;
+interface ResponseListToolbarProps {
+ selectedResponse: SelectedResponse | null;
searchInput: string;
isFetching: boolean;
- clearSelection: () => void;
+ onClearSelection: () => void;
onSearch: (searchInput: string) => void;
}
-function ResponseListToolbar(props: Props) {
- const { selected, searchInput, isFetching, clearSelection, onSearch } = props;
+function ResponseListToolbar(props: ResponseListToolbarProps) {
+ const { selectedResponse, searchInput, isFetching, onClearSelection, onSearch } = props;
- const { selectedQuestionnaire, existingResponses, clearSelectedQuestionnaire } = useContext(
- SelectedQuestionnaireContext
- );
- const patient = useConfigStore((state) => state.patient);
- const theme = useTheme();
+ const { selectedQuestionnaire, existingResponses } = useContext(SelectedQuestionnaireContext);
+
+ const isTabletAndUp = useResponsive('up', 'md');
const selectedQuestionnaireTitle =
selectedQuestionnaire?.listItem.title ?? 'selected questionnaire';
- const toolBarColors = getToolBarColors(selected, selectedQuestionnaire, existingResponses);
+ const selected = selectedResponse?.listItem;
+
+ const toolBarColors = getResponseToolBarColors(
+ selected,
+ selectedQuestionnaire,
+ existingResponses
+ );
return (
<>
@@ -68,9 +65,12 @@ function ResponseListToolbar(props: Props) {
{selected.title} — {dayjs(selected.authored).format('LL')} selected
) : selectedQuestionnaire && existingResponses.length > 0 ? (
-
- Displaying responses from the {selectedQuestionnaireTitle} questionnaire
-
+
+
+
+ Displaying responses from the {selectedQuestionnaireTitle} questionnaire
+
+
) : (
)}
- {selected ? (
-
-
-
-
-
- ) : selectedQuestionnaire && existingResponses.length > 0 ? (
-
- clearSelectedQuestionnaire()}
- data-test="button-remove-questionnaire-filter">
-
-
-
- ) : (
-
- Showing responses for {constructName(patient?.name)}
-
- )}
+
{isFetching ? : }
>
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbarButtons.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbarButtons.tsx
new file mode 100644
index 000000000..3f8226c74
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseListToolbarButtons.tsx
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useContext } from 'react';
+import { Box, IconButton, Stack, Typography } from '@mui/material';
+import ClearIcon from '@mui/icons-material/Clear';
+import Iconify from '../../../../../../components/Iconify/Iconify.tsx';
+import { constructName } from '../../../../../smartAppLaunch/utils/launchContext.ts';
+import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuestionnaireContext.tsx';
+import type { SelectedResponse } from '../../../../types/list.interface.ts';
+import useConfigStore from '../../../../../../stores/useConfigStore.ts';
+import OpenResponseButton from '../Buttons/OpenResponseButton.tsx';
+import useResponsive from '../../../../../../hooks/useResponsive.ts';
+
+interface ResponseListToolbarButtonsProps {
+ selectedResponse: SelectedResponse | null;
+ onClearSelection: () => void;
+}
+
+function ResponseListToolbarButtons(props: ResponseListToolbarButtonsProps) {
+ const { selectedResponse, onClearSelection } = props;
+
+ const { selectedQuestionnaire, existingResponses, clearSelectedQuestionnaire } = useContext(
+ SelectedQuestionnaireContext
+ );
+
+ const patient = useConfigStore((state) => state.patient);
+
+ const isDesktop = useResponsive('up', 'lg');
+
+ const selected = selectedResponse?.listItem;
+
+ if (selected) {
+ return (
+
+
+
+
+
+
+
+ Unselect
+
+
+
+ );
+ }
+
+ if (selectedQuestionnaire && existingResponses.length > 0) {
+ return (
+
+ clearSelectedQuestionnaire()}
+ data-test="button-remove-questionnaire-filter">
+
+
+
+ Remove filter
+
+
+ );
+ }
+
+ return (
+
+ Showing responses for {constructName(patient?.name)}
+
+ );
+}
+
+export default ResponseListToolbarButtons;
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseTableRow.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseTableRow.tsx
index d6b8534c0..0e44f797f 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseTableRow.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/TableComponents/ResponseTableRow.tsx
@@ -41,7 +41,7 @@ function ResponseTableRow(props: ResponseTableRowProps) {
tabIndex={-1}
selected={isSelected}
sx={{ cursor: 'pointer' }}
- data-test="questionnaire-list-row"
+ data-test="response-list-row"
onClick={onRowClick}>
diff --git a/apps/smart-forms-app/src/features/dashboard/hooks/useFetchQuestionnaires.ts b/apps/smart-forms-app/src/features/dashboard/hooks/useFetchQuestionnaires.ts
index 92ae251ff..762cb55da 100644
--- a/apps/smart-forms-app/src/features/dashboard/hooks/useFetchQuestionnaires.ts
+++ b/apps/smart-forms-app/src/features/dashboard/hooks/useFetchQuestionnaires.ts
@@ -17,19 +17,12 @@
import { useQuery } from '@tanstack/react-query';
import type { Bundle } from 'fhir/r4';
-import {
- constructBundle,
- getFormsServerBundlePromise,
- getQuestionnaireListItems
-} from '../utils/dashboard.ts';
+import { getFormsServerBundlePromise, getQuestionnaireListItems } from '../utils/dashboard.ts';
import { useMemo } from 'react';
-import { loadQuestionnairesFromLocal } from '../../../api/local.ts';
import type { QuestionnaireListItem } from '../types/list.interface.ts';
-import type { Source } from '../../../types/source.interface.ts';
interface useFetchQuestionnairesReturnParams {
remoteQuestionnaires: Bundle | undefined;
- localQuestionnaires: Bundle;
questionnaireListItems: QuestionnaireListItem[];
fetchStatus: 'error' | 'success' | 'loading';
fetchError: unknown;
@@ -39,8 +32,7 @@ interface useFetchQuestionnairesReturnParams {
function useFetchQuestionnaires(
searchInput: string,
- debouncedInput: string,
- questionnaireSource: Source
+ debouncedInput: string
): useFetchQuestionnairesReturnParams {
const numOfSearchEntries = 100;
@@ -59,24 +51,14 @@ function useFetchQuestionnaires(
enabled: debouncedInput === searchInput
});
- // load local questionnaires to be used if source is local
- const localQuestionnaires: Bundle = useMemo(
- () => constructBundle(loadQuestionnairesFromLocal()),
- []
- );
-
// construct questionnaire list items for data display
const questionnaireListItems: QuestionnaireListItem[] = useMemo(
- () =>
- getQuestionnaireListItems(
- questionnaireSource === 'remote' ? remoteQuestionnaires : localQuestionnaires
- ),
- [remoteQuestionnaires, localQuestionnaires, questionnaireSource]
+ () => getQuestionnaireListItems(remoteQuestionnaires),
+ [remoteQuestionnaires]
);
return {
remoteQuestionnaires,
- localQuestionnaires,
questionnaireListItems,
fetchStatus: status,
fetchError: error,
diff --git a/apps/smart-forms-app/src/features/dashboard/hooks/useFetchResponses.ts b/apps/smart-forms-app/src/features/dashboard/hooks/useFetchResponses.ts
index 7e5c4830f..6edc009b8 100644
--- a/apps/smart-forms-app/src/features/dashboard/hooks/useFetchResponses.ts
+++ b/apps/smart-forms-app/src/features/dashboard/hooks/useFetchResponses.ts
@@ -25,7 +25,6 @@ import {
import { useContext, useMemo } from 'react';
import type { ResponseListItem } from '../types/list.interface.ts';
import useConfigStore from '../../../stores/useConfigStore.ts';
-import type { Source } from '../../../types/source.interface.ts';
import { SelectedQuestionnaireContext } from '../contexts/SelectedQuestionnaireContext.tsx';
interface useFetchResponsesReturnParams {
@@ -38,8 +37,7 @@ interface useFetchResponsesReturnParams {
function useFetchResponses(
searchInput: string,
- debouncedInput: string,
- questionnaireSource: Source
+ debouncedInput: string
): useFetchResponsesReturnParams {
const smartClient = useConfigStore((state) => state.smartClient);
const patient = useConfigStore((state) => state.patient);
@@ -63,7 +61,7 @@ function useFetchResponses(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
() => getClientBundlePromise(smartClient!, queryUrl),
{
- enabled: questionnaireSource === 'remote' && !!smartClient && debouncedInput === searchInput
+ enabled: !!smartClient && debouncedInput === searchInput
}
);
diff --git a/apps/smart-forms-app/src/features/dashboard/layout/DashboardLayout.tsx b/apps/smart-forms-app/src/features/dashboard/layout/DashboardLayout.tsx
index e2ccce6ce..1a4c3e429 100644
--- a/apps/smart-forms-app/src/features/dashboard/layout/DashboardLayout.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/layout/DashboardLayout.tsx
@@ -16,7 +16,7 @@
*/
import { useEffect, useState } from 'react';
-import DashboardHeader from '../components/DashboardHeader/DashboardHeader.tsx';
+import GenericHeader from '../../../components/Header/GenericHeader.tsx';
import DashboardNav from '../components/DashboardNav/DashboardNav.tsx';
import { Main, StyledRoot } from '../../../components/Layout/Layout.styles.ts';
import { Outlet, useNavigate } from 'react-router-dom';
@@ -59,7 +59,7 @@ function DashboardLayout() {
return (
- setOpen(true)} />
+ setOpen(true)} />
setOpen(false)} />
diff --git a/apps/smart-forms-app/src/features/dashboard/types/list.interface.ts b/apps/smart-forms-app/src/features/dashboard/types/list.interface.ts
index 4510466cb..6323f33bd 100644
--- a/apps/smart-forms-app/src/features/dashboard/types/list.interface.ts
+++ b/apps/smart-forms-app/src/features/dashboard/types/list.interface.ts
@@ -5,7 +5,7 @@ export interface QuestionnaireListItem {
title: string;
avatarColor: string;
publisher: string;
- date: string;
+ date: Date | null;
status: Questionnaire['status'];
}
@@ -14,13 +14,10 @@ export interface ResponseListItem {
title: string;
avatarColor: string;
author: string;
- authored: string;
+ authored: Date | null;
status: QuestionnaireResponse['status'];
}
-export type ListItem = QuestionnaireListItem | ResponseListItem;
-export type ListItemWithIndex = [number, QuestionnaireListItem | ResponseListItem];
-
export interface SelectedQuestionnaire {
listItem: QuestionnaireListItem;
resource: Questionnaire;
diff --git a/apps/smart-forms-app/src/features/dashboard/utils/dashboard.ts b/apps/smart-forms-app/src/features/dashboard/utils/dashboard.ts
index 79abed5fd..17b9e1887 100644
--- a/apps/smart-forms-app/src/features/dashboard/utils/dashboard.ts
+++ b/apps/smart-forms-app/src/features/dashboard/utils/dashboard.ts
@@ -15,7 +15,6 @@
* limitations under the License.
*/
-import filter from 'lodash.filter';
import * as FHIR from 'fhirclient';
import type {
Bundle,
@@ -28,82 +27,11 @@ import randomColor from 'randomcolor';
import dayjs from 'dayjs';
import { getQuestionnaireNameFromResponse } from '../../renderer/utils/itemControl.ts';
import type Client from 'fhirclient/lib/Client';
-import type {
- ListItem,
- ListItemWithIndex,
- QuestionnaireListItem,
- ResponseListItem
-} from '../types/list.interface.ts';
+import type { QuestionnaireListItem, ResponseListItem } from '../types/list.interface.ts';
import { HEADERS } from '../../../api/headers.ts';
const endpointUrl = import.meta.env.VITE_FORMS_SERVER_URL ?? 'https://api.smartforms.io/fhir';
-export function descendingComparator(
- a: ListItem,
- b: ListItem,
- orderBy: keyof QuestionnaireListItem | keyof ResponseListItem,
- listType: 'questionnaire' | 'response'
-): number {
- if (listType === 'questionnaire') {
- orderBy = orderBy as keyof QuestionnaireListItem;
- a = a as QuestionnaireListItem;
- b = b as QuestionnaireListItem;
- if (b[orderBy] < a[orderBy]) {
- return -1;
- }
- if (b[orderBy] > a[orderBy]) {
- return 1;
- }
- } else {
- orderBy = orderBy as keyof ResponseListItem;
- a = a as ResponseListItem;
- b = b as ResponseListItem;
- if (b[orderBy] < a[orderBy]) {
- return -1;
- }
- if (b[orderBy] > a[orderBy]) {
- return 1;
- }
- }
-
- return 0;
-}
-
-export function getComparator(
- order: 'asc' | 'desc',
- orderBy: keyof QuestionnaireListItem | keyof ResponseListItem,
- listType: 'questionnaire' | 'response'
-): (a: ListItem, b: ListItem) => number {
- return order === 'desc'
- ? (a: ListItem, b: ListItem) => descendingComparator(a, b, orderBy, listType)
- : (a: ListItem, b: ListItem) => -descendingComparator(a, b, orderBy, listType);
-}
-
-export function applySortFilter(
- array: ListItem[],
- comparator: (a: ListItem, b: ListItem) => number,
- source: 'local' | 'remote',
- query?: string
-): ListItem[] {
- const stabilizedThis: ListItemWithIndex[] = array.map((el, index) => [index, el]);
-
- stabilizedThis.sort((a: ListItemWithIndex, b: ListItemWithIndex) => {
- const order = comparator(a[1], b[1]);
- if (order !== 0) {
- return order;
- }
-
- return a[0] - b[0];
- });
-
- // Perform client-side filtering only when source is local
- if (query && source === 'local') {
- return filter(array, (_item) => _item.title.toLowerCase().indexOf(query.toLowerCase()) !== -1);
- }
-
- return stabilizedThis.map((el) => el[1]);
-}
-
export function getFormsServerBundlePromise(queryUrl: string): Promise {
queryUrl = queryUrl.replace('|', '&version=');
@@ -163,14 +91,20 @@ export function getQuestionnaireListItems(bundle: Bundle | undefined): Questionn
})
.map((entry, i) => {
const questionnaire = entry.resource as Questionnaire; // non-questionnaire resources are filtered
- const questionnaireTitle = questionnaire.title ?? 'Untitled';
+ const questionnaireTitle = questionnaire.title
+ ? questionnaire.title.charAt(0).toUpperCase() + questionnaire.title.slice(1)
+ : 'Untitled';
+
+ const questionnairePublisher = questionnaire.publisher
+ ? questionnaire.publisher.charAt(0).toUpperCase() + questionnaire.publisher.slice(1)
+ : '-';
const questionnaireListItem: QuestionnaireListItem = {
id: questionnaire.id ?? i.toString(),
title: questionnaireTitle,
avatarColor: randomColor({ luminosity: 'dark', seed: questionnaireTitle + i.toString() }),
- publisher: questionnaire.publisher ?? '—',
- date: questionnaire.date ? dayjs(questionnaire.date).format() : '—',
+ publisher: questionnairePublisher,
+ date: questionnaire.date ? dayjs(questionnaire.date).toDate() : null,
status: questionnaire.status
};
return questionnaireListItem;
@@ -191,7 +125,7 @@ export function getResponseListItems(bundle: Bundle | undefined): ResponseListIt
title: responseTitle,
avatarColor: randomColor({ luminosity: 'dark', seed: responseTitle + i.toString() }),
author: response.author?.display ?? '—',
- authored: response.authored ?? dayjs(response.authored).format(),
+ authored: response.authored ? dayjs(response.authored).toDate() : null,
status: response.status
};
return responseListItem;
diff --git a/apps/smart-forms-app/src/features/dashboard/utils/tableColumns.ts b/apps/smart-forms-app/src/features/dashboard/utils/tableColumns.ts
new file mode 100644
index 000000000..6d04be450
--- /dev/null
+++ b/apps/smart-forms-app/src/features/dashboard/utils/tableColumns.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { ColumnDef } from '@tanstack/react-table';
+import type { QuestionnaireListItem, ResponseListItem } from '../types/list.interface.ts';
+
+export function createQuestionnaireTableColumns(): ColumnDef[] {
+ return [
+ {
+ accessorKey: 'title',
+ header: 'Title'
+ },
+ {
+ accessorKey: 'publisher',
+ header: 'Publisher'
+ },
+ {
+ accessorKey: 'date',
+ header: 'Date'
+ },
+ {
+ accessorKey: 'status',
+ header: 'Status'
+ }
+ ];
+}
+
+export function createResponseTableColumns(): ColumnDef[] {
+ return [
+ {
+ accessorKey: 'title',
+ header: 'Questionnaire Title'
+ },
+ {
+ accessorKey: 'author',
+ header: 'Author'
+ },
+ {
+ accessorKey: 'authored',
+ header: 'Authored On'
+ },
+ {
+ accessorKey: 'status',
+ header: 'Status'
+ }
+ ];
+}
diff --git a/apps/smart-forms-app/src/features/playground/components/PlaygroundHeader.tsx b/apps/smart-forms-app/src/features/playground/components/PlaygroundHeader.tsx
index 48db086fa..b2cf8a4a5 100644
--- a/apps/smart-forms-app/src/features/playground/components/PlaygroundHeader.tsx
+++ b/apps/smart-forms-app/src/features/playground/components/PlaygroundHeader.tsx
@@ -15,17 +15,14 @@
* limitations under the License.
*/
-import Iconify from '../../../components/Iconify/Iconify.tsx';
-
import { useTheme } from '@mui/material/styles';
import Logo from '../../../components/Logos/Logo.tsx';
-import { Box, IconButton, Toolbar, Tooltip, Typography } from '@mui/material';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { LogoWrapper } from '../../../components/Logos/Logo.styles.ts';
-import { StyledRoot } from '../../../components/Header/Header.styles.ts';
+import { StyledRoot, StyledToolbar } from '../../../components/Header/Header.styles.ts';
import { memo } from 'react';
-
-const HEADER_PLAYGROUND = 64;
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
const PlaygroundHeader = memo(function PlaygroundHeader() {
const theme = useTheme();
@@ -34,10 +31,7 @@ const PlaygroundHeader = memo(function PlaygroundHeader() {
return (
-
+
{
@@ -47,7 +41,7 @@ const PlaygroundHeader = memo(function PlaygroundHeader() {
mr: 1,
color: 'text.primary'
}}>
-
+
@@ -56,8 +50,11 @@ const PlaygroundHeader = memo(function PlaygroundHeader() {
- Playground
-
+
+
+ Playground
+
+
);
});
diff --git a/apps/smart-forms-app/src/features/preview/utils/preview.ts b/apps/smart-forms-app/src/features/preview/utils/preview.ts
index 3a7d1f936..06e8189d3 100644
--- a/apps/smart-forms-app/src/features/preview/utils/preview.ts
+++ b/apps/smart-forms-app/src/features/preview/utils/preview.ts
@@ -30,7 +30,7 @@ export function qrToHTML(
): string {
if (!questionnaireResponse.item || questionnaireResponse.item.length === 0) return '';
- let QrHtml = `${questionnaire.title}
`;
+ let QrHtml = `${questionnaire.title}
`;
for (const topLevelQRItem of questionnaireResponse.item) {
const topLevelQRItemHTML = qrItemToHTML(topLevelQRItem);
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DateTimeItem/DateTimeField.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DateTimeItem/DateTimeField.tsx
index 024ce1e49..26d7ff049 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DateTimeItem/DateTimeField.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DateTimeItem/DateTimeField.tsx
@@ -19,6 +19,7 @@ import type { PropsWithIsTabledAttribute } from '../../../../types/renderProps.i
import type { Dayjs } from 'dayjs';
import { DateTimePicker as MuiDateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { Box } from '@mui/material';
interface DateTimeFieldProps extends PropsWithIsTabledAttribute {
value: Dayjs | null;
@@ -33,20 +34,21 @@ function DateTimeField(props: DateTimeFieldProps) {
return (
-
+
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemChoice/QItemChoiceSelectAnswerOption.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemChoice/QItemChoiceSelectAnswerOption.tsx
index 70bbfccef..44d9ce514 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemChoice/QItemChoiceSelectAnswerOption.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemChoice/QItemChoiceSelectAnswerOption.tsx
@@ -30,6 +30,7 @@ import type {
PropsWithQrItemChangeHandler
} from '../../../../types/renderProps.interface.ts';
import DisplayInstructions from '../DisplayItem/DisplayInstructions.tsx';
+import { Fragment } from 'react';
interface Props
extends PropsWithQrItemChangeHandler,
@@ -77,28 +78,32 @@ function QItemChoiceSelectAnswerOption(props: Props) {
endAdornment={{displayUnit}}
sx={{ maxWidth: !isTabled ? 280 : 3000 }}
onChange={handleChange}>
- {qItem.answerOption?.map((option) => {
+ {qItem.answerOption?.map((option, index) => {
if (option['valueCoding']) {
return (
);
- } else if (option['valueString']) {
+ }
+
+ if (option['valueString']) {
return (
);
- } else if (option['valueInteger']) {
+ }
+
+ if (option['valueInteger']) {
return (
);
- } else {
- return null;
}
+
+ return ;
})}
);
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPreviewPage/FormPreview.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPreviewPage/FormPreview.tsx
index b3c269421..18a33b8e9 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPreviewPage/FormPreview.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPreviewPage/FormPreview.tsx
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Box, Card, Container, Fade, Typography } from '@mui/material';
+import { Card, Container, Fade } from '@mui/material';
import FormInvalid from '../FormPage/FormInvalid.tsx';
import parse from 'html-react-parser';
import { qrToHTML } from '../../../preview/utils/preview.ts';
@@ -23,6 +23,7 @@ import { removeHiddenAnswers } from '../../../save/api/saveQr.ts';
import { Helmet } from 'react-helmet';
import useQuestionnaireStore from '../../../../stores/useQuestionnaireStore.ts';
import useQuestionnaireResponseStore from '../../../../stores/useQuestionnaireResponseStore.ts';
+import PageHeading from '../../../dashboard/components/DashboardPages/PageHeading.tsx';
function FormPreview() {
const sourceQuestionnaire = useQuestionnaireStore((state) => state.sourceQuestionnaire);
@@ -55,10 +56,8 @@ function FormPreview() {
{sourceQuestionnaire.title ?? 'Form Preview'}
-
-
- Preview
-
+
+ Preview
{parsedHTML}
diff --git a/apps/smart-forms-app/src/features/renderer/components/RendererHeader/RendererHeader.tsx b/apps/smart-forms-app/src/features/renderer/components/RendererHeader/RendererHeader.tsx
index 7c6556b8c..b34476d21 100644
--- a/apps/smart-forms-app/src/features/renderer/components/RendererHeader/RendererHeader.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/RendererHeader/RendererHeader.tsx
@@ -17,16 +17,15 @@
import Iconify from '../../../../components/Iconify/Iconify.tsx';
import { useTheme } from '@mui/material/styles';
-import DesktopHeader from '../../../../components/Header/DesktopHeader.tsx';
import useResponsive from '../../../../hooks/useResponsive.ts';
import Logo from '../../../../components/Logos/Logo.tsx';
-import { Box, IconButton, Stack, Typography } from '@mui/material';
+import { Box, IconButton, Typography } from '@mui/material';
import UpdatingIndicator from './UpdatingIndicator.tsx';
-import MobileHeaderWithQuestionnaire from '../../../../components/Header/MobileHeaderWithQuestionnaire.tsx';
import { LogoWrapper } from '../../../../components/Logos/Logo.styles.ts';
import { StyledRoot, StyledToolbar } from '../../../../components/Header/Header.styles.ts';
import useQuestionnaireStore from '../../../../stores/useQuestionnaireStore.ts';
import { memo } from 'react';
+import HeaderIcons from '../../../../components/Header/HeaderIcons.tsx';
interface RendererHeaderProps {
navIsCollapsed: boolean;
@@ -49,7 +48,6 @@ const RendererHeader = memo(function RendererHeader(props: RendererHeaderProps)
)}
- {isDesktop && navIsExpanded ? (
-
-
- {sourceQuestionnaire.title}
-
-
- ) : null}
+
+
+ {sourceQuestionnaire.title}
+
+
+
-
- {isDesktop && navIsExpanded ? : }
-
+
);
diff --git a/apps/smart-forms-app/src/features/renderer/components/RendererHeader/UpdatingIndicator.tsx b/apps/smart-forms-app/src/features/renderer/components/RendererHeader/UpdatingIndicator.tsx
index f709d9718..40252b0b6 100644
--- a/apps/smart-forms-app/src/features/renderer/components/RendererHeader/UpdatingIndicator.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/RendererHeader/UpdatingIndicator.tsx
@@ -18,12 +18,15 @@
import { useEffect, useState } from 'react';
import { Fade, Typography } from '@mui/material';
import useQuestionnaireResponseStore from '../../../../stores/useQuestionnaireResponseStore.ts';
+import useResponsive from '../../../../hooks/useResponsive.ts';
function UpdatingIndicator() {
const updatableResponse = useQuestionnaireResponseStore((state) => state.updatableResponse);
const [isUpdating, setIsUpdating] = useState(false);
+ const isDesktop = useResponsive('up', 'lg');
+
useEffect(() => {
setIsUpdating(true);
setTimeout(() => {
@@ -33,7 +36,11 @@ function UpdatingIndicator() {
return (
-
+
Updating...
diff --git a/apps/smart-forms-app/src/features/renderer/components/RendererNav/RendererNav.tsx b/apps/smart-forms-app/src/features/renderer/components/RendererNav/RendererNav.tsx
index b4348d41a..d1279cc51 100644
--- a/apps/smart-forms-app/src/features/renderer/components/RendererNav/RendererNav.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/RendererNav/RendererNav.tsx
@@ -28,8 +28,7 @@ import CsiroLogo from '../../../../components/Logos/CsiroLogo.tsx';
import { NavLogoWrapper } from '../../../../components/Logos/Logo.styles.ts';
import { NavErrorAlertWrapper } from '../../../../components/Nav/Nav.styles.ts';
import useConfigStore from '../../../../stores/useConfigStore.ts';
-
-const NAV_WIDTH = 240;
+import { NAV_WIDTH } from '../../../../components/Header/Header.styles.ts';
interface Props {
openNav: boolean;
@@ -54,7 +53,7 @@ function RendererNav(props: Props) {
'& .simplebar-content': { height: 1, display: 'flex', flexDirection: 'column' }
}}>
-
+
diff --git a/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx b/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx
index c7801db5e..3600ac868 100644
--- a/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx
+++ b/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx
@@ -75,7 +75,6 @@ function Authorisation() {
const setPatient = useConfigStore((state) => state.setPatient);
const setUser = useConfigStore((state) => state.setUser);
const setEncounter = useConfigStore((state) => state.setEncounter);
- const updateQuestionnaireSource = useConfigStore((state) => state.updateQuestionnaireSource);
const buildSourceQuestionnaire = useQuestionnaireStore((state) => state.buildSourceQuestionnaire);
@@ -89,7 +88,6 @@ function Authorisation() {
.then((client) => {
// Set SMART client
setSmartClient(client);
- updateQuestionnaireSource('remote');
sessionStorage.setItem('authorised', 'true');
dispatch({ type: 'UPDATE_HAS_CLIENT', payload: true });
diff --git a/apps/smart-forms-app/src/features/smartAppLaunch/components/LaunchView.tsx b/apps/smart-forms-app/src/features/smartAppLaunch/components/LaunchView.tsx
index 2bd284045..86c5b345b 100644
--- a/apps/smart-forms-app/src/features/smartAppLaunch/components/LaunchView.tsx
+++ b/apps/smart-forms-app/src/features/smartAppLaunch/components/LaunchView.tsx
@@ -17,9 +17,9 @@
import ProgressSpinner from '../../../components/Spinners/ProgressSpinner.tsx';
import type { LaunchState } from './Launch.tsx';
-import { useNavigate } from 'react-router-dom';
-import { Button, Stack, Typography } from '@mui/material';
+import { Stack, Typography } from '@mui/material';
import CenteredWrapper from '../../../components/Wrapper/CenteredWrapper.tsx';
+import UnlaunchedButton from '../../../components/Button/UnlaunchedButton.tsx';
interface LaunchViewProps {
launchState: LaunchState;
@@ -28,8 +28,6 @@ interface LaunchViewProps {
function LaunchView(props: LaunchViewProps) {
const { launchState } = props;
- const navigate = useNavigate();
-
if (launchState === 'error') {
return (
@@ -41,15 +39,8 @@ function LaunchView(props: LaunchViewProps) {
{'Please contact your administrator for assistance.'}
-
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/smartAppLaunch/components/RenderAuthStatus.tsx b/apps/smart-forms-app/src/features/smartAppLaunch/components/RenderAuthStatus.tsx
index 4a9c509e4..fe98bdc51 100644
--- a/apps/smart-forms-app/src/features/smartAppLaunch/components/RenderAuthStatus.tsx
+++ b/apps/smart-forms-app/src/features/smartAppLaunch/components/RenderAuthStatus.tsx
@@ -16,20 +16,12 @@
*/
import { useState } from 'react';
-import {
- Accordion,
- AccordionDetails,
- AccordionSummary,
- Box,
- Button,
- Typography
-} from '@mui/material';
-import ErrorIcon from '@mui/icons-material/Error';
+import { Accordion, AccordionDetails, AccordionSummary, Stack, Typography } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
-import Iconify from '../../../components/Iconify/Iconify.tsx';
import ProgressSpinner from '../../../components/Spinners/ProgressSpinner.tsx';
-import { useNavigate } from 'react-router-dom';
import type { AuthState } from '../types/authorisation.interface.ts';
+import CenteredWrapper from '../../../components/Wrapper/CenteredWrapper.tsx';
+import UnlaunchedButton from '../../../components/Button/UnlaunchedButton.tsx';
interface RenderAuthStatusProps {
authState: AuthState;
@@ -40,19 +32,18 @@ function RenderAuthStatus(props: RenderAuthStatusProps) {
const [isExpanded, setIsExpanded] = useState(false);
- const navigate = useNavigate();
+ const launchFailed =
+ authState.hasClient === false || authState.hasUser === false || authState.hasPatient === false;
- if (
- authState.hasClient === false ||
- authState.hasUser === false ||
- authState.hasPatient === false
- ) {
+ if (launchFailed) {
return (
-
-
-
- Launch failed.
-
+
+
+ An error occurred while authorising the launch.
+
+ {'Try relaunching the app or contact your administrator for assistance.'}
+
+
{authState.errorMessage ? (
setIsExpanded(expanded)}>
@@ -64,19 +55,8 @@ function RenderAuthStatus(props: RenderAuthStatusProps) {
) : null}
-
-
- }
- data-test="button-create-response"
- onClick={() => {
- navigate('/dashboard/questionnaires');
- }}>
- Proceed to app anyway
-
-
-
+
+
);
}
diff --git a/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx b/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx
index d9cf1fa12..c43cae978 100644
--- a/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx
+++ b/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx
@@ -16,7 +16,7 @@
*/
import { useContext, useEffect, useRef } from 'react';
-import { Box, Card, Container, Fade, Typography } from '@mui/material';
+import { Box, Card, Container, Fade } from '@mui/material';
import ViewerInvalid from '../renderer/components/FormPage/ViewerInvalid.tsx';
import { PrintComponentRefContext } from './ViewerLayout.tsx';
import { removeHiddenAnswers } from '../save/api/saveQr.ts';
@@ -25,6 +25,7 @@ import { qrToHTML } from '../preview/utils/preview.ts';
import { Helmet } from 'react-helmet';
import useQuestionnaireStore from '../../stores/useQuestionnaireStore.ts';
import useQuestionnaireResponseStore from '../../stores/useQuestionnaireResponseStore.ts';
+import PageHeading from '../dashboard/components/DashboardPages/PageHeading.tsx';
function ResponsePreview() {
const sourceQuestionnaire = useQuestionnaireStore((state) => state.sourceQuestionnaire);
@@ -59,7 +60,6 @@ function ResponsePreview() {
enableWhenExpressions
});
- // TODO fix the additional group title "Emergency contact"
const parsedHTML = parse(qrToHTML(questionnaire, responseCleaned));
return (
@@ -68,10 +68,8 @@ function ResponsePreview() {
{questionnaire.title ? questionnaire.title : 'Response Preview'}
-
-
- Response Preview
-
+
+ Response Preview
<>{parsedHTML}>
diff --git a/apps/smart-forms-app/src/features/viewer/ViewerHeader/ViewerHeader.tsx b/apps/smart-forms-app/src/features/viewer/ViewerHeader/ViewerHeader.tsx
deleted file mode 100644
index ad229133a..000000000
--- a/apps/smart-forms-app/src/features/viewer/ViewerHeader/ViewerHeader.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2023 Commonwealth Scientific and Industrial Research
- * Organisation (CSIRO) ABN 41 687 119 230.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import Iconify from '../../../components/Iconify/Iconify.tsx';
-
-import { useTheme } from '@mui/material/styles';
-import DesktopHeader from '../../../components/Header/DesktopHeader.tsx';
-import useResponsive from '../../../hooks/useResponsive.ts';
-import Logo from '../../../components/Logos/Logo.tsx';
-import { Box, Stack } from '@mui/material';
-import {
- MenuIconButton,
- StyledRoot,
- StyledToolbar
-} from '../../../components/Header/Header.styles.ts';
-import { LogoWrapper } from '../../../components/Logos/Logo.styles.ts';
-import MobileHeaderWithQuestionnaire from '../../../components/Header/MobileHeaderWithQuestionnaire.tsx';
-import { memo } from 'react';
-
-interface ViewerHeaderProps {
- onOpenNav: () => void;
-}
-
-const ViewerHeader = memo(function ViewerHeader(props: ViewerHeaderProps) {
- const { onOpenNav } = props;
-
- const theme = useTheme();
-
- const isDesktop = useResponsive('up', 'lg');
-
- return (
-
-
-
-
-
-
- {!isDesktop ? (
-
-
-
- ) : null}
-
-
-
-
- {isDesktop ? : }
-
-
-
- );
-});
-
-export default ViewerHeader;
diff --git a/apps/smart-forms-app/src/features/viewer/ViewerLayout.tsx b/apps/smart-forms-app/src/features/viewer/ViewerLayout.tsx
index 43036556d..69f48eff5 100644
--- a/apps/smart-forms-app/src/features/viewer/ViewerLayout.tsx
+++ b/apps/smart-forms-app/src/features/viewer/ViewerLayout.tsx
@@ -22,10 +22,10 @@ import { Outlet } from 'react-router-dom';
import BackToTopButton from '../backToTop/components/BackToTopButton.tsx';
import { Fab } from '@mui/material';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
-import ViewerHeader from './ViewerHeader/ViewerHeader.tsx';
import ViewerNav from './ViewerNav/ViewerNav.tsx';
import type { PrintComponentRefContextType } from '../print/types/printComponentRefContext.type.ts';
+import GenericHeader from '../../components/Header/GenericHeader.tsx';
export const PrintComponentRefContext = createContext({
componentRef: null,
@@ -40,7 +40,7 @@ function ViewerLayout() {
return (
- setOpen(true)} />
+ setOpen(true)} />
setOpen(false)} />
diff --git a/apps/smart-forms-app/src/features/viewer/ViewerNav/ViewerNav.tsx b/apps/smart-forms-app/src/features/viewer/ViewerNav/ViewerNav.tsx
index f158c44b1..03ae39602 100644
--- a/apps/smart-forms-app/src/features/viewer/ViewerNav/ViewerNav.tsx
+++ b/apps/smart-forms-app/src/features/viewer/ViewerNav/ViewerNav.tsx
@@ -29,8 +29,7 @@ import { NavErrorAlertWrapper } from '../../../components/Nav/Nav.styles.ts';
import useQuestionnaireStore from '../../../stores/useQuestionnaireStore.ts';
import useConfigStore from '../../../stores/useConfigStore.ts';
import useQuestionnaireResponseStore from '../../../stores/useQuestionnaireResponseStore.ts';
-
-const NAV_WIDTH = 240;
+import { NAV_WIDTH } from '../../../components/Header/Header.styles.ts';
interface Props {
openNav: boolean;
@@ -56,7 +55,7 @@ function ViewerNav(props: Props) {
'& .simplebar-content': { height: 1, display: 'flex', flexDirection: 'column' }
}}>
-
+
diff --git a/apps/smart-forms-app/src/router/Router.tsx b/apps/smart-forms-app/src/router/Router.tsx
index a46a8392d..37f1b251b 100644
--- a/apps/smart-forms-app/src/router/Router.tsx
+++ b/apps/smart-forms-app/src/router/Router.tsx
@@ -19,7 +19,6 @@ import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'
import DashboardLayout from '../features/dashboard/layout/DashboardLayout.tsx';
import Launch from '../features/smartAppLaunch/components/Launch.tsx';
import QuestionnairesPage from '../features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnairesPage.tsx';
-import ResponsesPage from '../features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx';
import RendererLayout from '../features/renderer/components/RendererLayout.tsx';
import FormRenderer from '../features/renderer/components/FormPage/FormRenderer/FormRenderer.tsx';
import FormPreview from '../features/renderer/components/FormPreviewPage/FormPreview.tsx';
@@ -28,6 +27,7 @@ import ResponsePreview from '../features/viewer/ResponsePreview.tsx';
import Authorisation from '../features/smartAppLaunch/components/Authorisation.tsx';
import PlaygroundLayout from '../features/playground/components/PlaygroundLayout.tsx';
import Playground from '../features/playground/components/Playground.tsx';
+import ResponsesPage from '../features/dashboard/components/DashboardPages/ResponsesPage/ResponsesPage.tsx';
export default function Router() {
const router = createBrowserRouter([
diff --git a/apps/smart-forms-app/src/stores/useConfigStore.ts b/apps/smart-forms-app/src/stores/useConfigStore.ts
index 6bbe0118f..a7837b12f 100644
--- a/apps/smart-forms-app/src/stores/useConfigStore.ts
+++ b/apps/smart-forms-app/src/stores/useConfigStore.ts
@@ -1,7 +1,6 @@
import { create } from 'zustand';
import type { Encounter, Patient, Practitioner } from 'fhir/r4';
import type Client from 'fhirclient/lib/Client';
-import type { Source } from '../types/source.interface.ts';
export interface ConfigState {
smartClient: Client | null;
@@ -9,12 +8,10 @@ export interface ConfigState {
user: Practitioner | null;
encounter: Encounter | null;
debugMode: boolean;
- questionnaireSource: Source;
setSmartClient: (client: Client) => void;
setPatient: (patient: Patient) => void;
setUser: (user: Practitioner) => void;
setEncounter: (encounter: Encounter) => void;
- updateQuestionnaireSource: (newSource: Source) => void;
activateDebugMode: () => void;
}
@@ -24,12 +21,10 @@ const useConfigStore = create()((set) => ({
user: null,
encounter: null,
debugMode: false,
- questionnaireSource: 'remote',
setSmartClient: (client: Client) => set(() => ({ smartClient: client })),
setPatient: (patient: Patient) => set(() => ({ patient: patient })),
setUser: (user: Practitioner) => set(() => ({ user: user })),
setEncounter: (encounter: Encounter) => set(() => ({ encounter: encounter })),
- updateQuestionnaireSource: (newSource: Source) => set(() => ({ questionnaireSource: newSource })),
activateDebugMode: () => set(() => ({ debugMode: true }))
}));
diff --git a/apps/smart-forms-app/src/theme/palette.ts b/apps/smart-forms-app/src/theme/palette.ts
index 84bde3112..0fc33dabc 100644
--- a/apps/smart-forms-app/src/theme/palette.ts
+++ b/apps/smart-forms-app/src/theme/palette.ts
@@ -69,7 +69,7 @@ const palette: PaletteOptions = {
dark: '#abebc6'
},
pale: {
- primary: '#D1E9FC',
+ primary: '#D6EBFC',
secondary: '#D3EBDD'
},
divider: alpha(grey['500'], 0.24),
diff --git a/apps/smart-forms-app/src/types/Nav.interface.ts b/apps/smart-forms-app/src/types/Nav.interface.ts
index ffca2d9fc..3d8c2429c 100644
--- a/apps/smart-forms-app/src/types/Nav.interface.ts
+++ b/apps/smart-forms-app/src/types/Nav.interface.ts
@@ -15,16 +15,11 @@
* limitations under the License.
*/
+import type { ReactNode } from 'react';
+
export interface OperationItem {
title: string;
- icon: JSX.Element;
+ icon: ReactNode;
disabled?: boolean;
onClick: () => unknown;
}
-
-export interface NavItem {
- title: string;
- path: string;
- icon: JSX.Element;
- disabled?: boolean;
-}
diff --git a/package-lock.json b/package-lock.json
index 2a9351242..88831ce0e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
"@sentry/react": "^7.58.1",
"@sentry/tracing": "^7.57.0",
"@tanstack/react-query": "^4.29.5",
+ "@tanstack/react-table": "^8.9.3",
"allotment": "^1.19.0",
"dayjs": "^1.11.7",
"fhirclient": "^2.5.2",
@@ -7091,6 +7092,37 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/@tanstack/react-table": {
+ "version": "8.9.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.9.3.tgz",
+ "integrity": "sha512-Ng9rdm3JPoSCi6cVZvANsYnF+UoGVRxflMb270tVj0+LjeT/ZtZ9ckxF6oLPLcKesza6VKBqtdF9mQ+vaz24Aw==",
+ "dependencies": {
+ "@tanstack/table-core": "8.9.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
+ "node_modules/@tanstack/table-core": {
+ "version": "8.9.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.9.3.tgz",
+ "integrity": "sha512-NpHZBoHTfqyJk0m/s/+CSuAiwtebhYK90mDuf5eylTvgViNOujiaOaxNDxJkQQAsVvHWZftUGAx1EfO1rkKtLg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
diff --git a/package.json b/package.json
index cd36a2f0a..2eff6a66b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"scripts": {
"lint": "eslint -c .eslintrc.json .",
+ "lint-fix": "eslint -c .eslintrc.json --fix .",
"check-formatting": "prettier --check ."
},
"workspaces": [