Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6913 lock version conflict mitigation #986

Merged
merged 9 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ module.exports = {
sourceType: 'module',
},
overrides: [
{
// storybook files
files: ['*.stories.tsx'],
rules: {
'no-console': 'off',
},
},
{
files: ['*.ts', '*.tsx'],
extends: [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"cert": "rm -rf .cert && mkdir -p .cert && mkcert -key-file ./.cert/key.pem -cert-file ./.cert/cert.pem 'hmis.dev.test'",
"dedup": "yarn-deduplicate -s highest yarn.lock",
"postinstall": "husky install",
"storybook": "storybook dev -p 6006",
"storybook": "storybook dev -p 6006 --no-open",
"build-storybook": "storybook build",
"graphql:codegen": "graphql-codegen --config codegen.yml && node scripts/generateEnumDescriptions.js && prettier --write ./src/types/* graphql.schema.json"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/elements/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const ConfirmationDialog = ({
variant='gray'
data-testid='cancelDialogAction'
>
{cancelText || unconfirmable ? 'Close' : 'Cancel'}
{cancelText || (unconfirmable ? 'Close' : 'Cancel')}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like a bug where cancelText was not displayed

</Button>
)}
{!unconfirmable && (
Expand Down
85 changes: 85 additions & 0 deletions src/components/elements/SnackbarAlert.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Meta, StoryObj } from '@storybook/react';
import SnackbarAlert from './SnackbarAlert';

const meta: Meta<typeof SnackbarAlert> = {
component: SnackbarAlert,
parameters: {
layout: 'centered',
},
// Add controls for commonly adjusted props
argTypes: {
open: {
control: 'boolean',
description: 'Controls the visibility of the snackbar',
},
title: {
control: 'text',
description: 'Optional title text for the alert',
},
children: {
control: 'text',
description: 'Main content of the alert',
},
alertProps: {
control: 'object',
description: 'Props passed to the underlying MUI Alert component',
},
},
};

export default meta;

type Story = StoryObj<typeof SnackbarAlert>;

// Basic success message
export const Success: Story = {
args: {
open: true,
onClose: () => console.info('Close clicked'),
children: 'Operation completed successfully',
alertProps: {
severity: 'success',
},
},
};

// Error message with title
export const Error: Story = {
args: {
open: true,
onClose: () => console.log('Close clicked'),
title: 'Error',
children: 'Something went wrong. Please try again.',
alertProps: {
severity: 'error',
},
},
};

// Example with no title
export const NoTitle: Story = {
args: {
open: true,
onClose: () => console.log('Close clicked'),
children: 'Simple notification without a title',
alertProps: {
severity: 'success',
},
},
};

// Example with longer content
export const LongContent: Story = {
args: {
open: true,
onClose: () => console.log('Close clicked'),
title: 'System Notification',
children: `This is a longer notification message that demonstrates how the SnackbarAlert
component handles multiple lines of text. It might contain important information that
needs more space to be properly communicated to the user.`,
alertProps: {
severity: 'info',
sx: { height: '100%' },
},
},
};
40 changes: 40 additions & 0 deletions src/components/elements/SnackbarAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Alert, AlertProps, AlertTitle, Snackbar } from '@mui/material';

import { ReactNode } from 'react';

interface Props {
open: boolean;
onClose: VoidFunction;
children?: ReactNode;
alertProps?: AlertProps;
title?: string;
}
const SnackbarAlert: React.FC<Props> = ({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd use this in the existing Apollo Alert but refactoring that felt like a bit too much scope creep for this branch

open,
onClose,
children,
alertProps,
title,
}) => {
return (
<Snackbar
TransitionProps={{ appear: false }} // disabled transition since it looks a bit junky
open={open}
onClose={onClose}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
sx={({ shadows }) => ({
boxShadow: shadows[6],
borderRadius: 2, // matches Alert
'.MuiAlertTitle-root': { fontWeight: 600 },
marginTop: '100px',
})}
>
<Alert onClose={onClose} {...alertProps}>
{title && <AlertTitle sx={{ mb: 0 }}>{title}</AlertTitle>}
{children}
</Alert>
</Snackbar>
);
};

export default SnackbarAlert;
26 changes: 17 additions & 9 deletions src/modules/assessments/components/IndividualAssessmentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import NotFound from '@/components/pages/NotFound';
import useSafeParams from '@/hooks/useSafeParams';
import IndividualAssessmentFormController from '@/modules/assessments/components/IndividualAssessmentFormController';
import useEnrollmentDashboardContext from '@/modules/enrollment/hooks/useEnrollmentDashboardContext';
import LocalVersionCoordinationPrompt from '@/modules/localVersionCoordination/LocalVersionCoordinationPrompt';
import { EnrollmentDashboardRoutes } from '@/routes/routes';
import { useGetAssessmentQuery } from '@/types/gqlTypes';

Expand Down Expand Up @@ -42,15 +43,22 @@ const IndividualAssessmentPage = () => {
if (!assessment) return <NotFound />;

return (
<IndividualAssessmentFormController
enrollment={enrollment}
client={client}
viewingDefinition={assessment.definition}
editingDefinition={
assessment.upgradedDefinitionForEditing || assessment.definition
}
assessment={assessment}
/>
<>
<LocalVersionCoordinationPrompt
ttoomey marked this conversation as resolved.
Show resolved Hide resolved
recordType='Assessment'
recordId={assessment.id}
currentVersion={assessment.lockVersion}
/>
<IndividualAssessmentFormController
enrollment={enrollment}
client={client}
viewingDefinition={assessment.definition}
editingDefinition={
assessment.upgradedDefinitionForEditing || assessment.definition
}
assessment={assessment}
/>
</>
);
};

Expand Down
44 changes: 44 additions & 0 deletions src/modules/client/components/ClientForceRefetchButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { LoadingButton } from '@mui/lab';
import { ButtonProps } from '@mui/material';
import React, { useCallback, useRef } from 'react';

import { useGetClientLazyQuery } from '@/types/gqlTypes';

interface Props extends Omit<ButtonProps, 'onClick'> {
clientId: string;
children: string;
onClick: VoidFunction | undefined;
}

// before on-click, refetch the client to ensure we have the latest values
const ClientForceRefetchButton: React.FC<Props> = ({
clientId,
children,
onClick,
...buttonProps
}) => {
const activeRequestClientId = useRef<string | null>(null);

const [getClient, { loading, error }] = useGetClientLazyQuery({
fetchPolicy: 'network-only',
});
if (error) throw error;

const handleClick = useCallback(() => {
activeRequestClientId.current = clientId;
getClient({ variables: { id: clientId } }).then(() => {
// Only trigger onClick if the clientId hasn't changed
if (onClick && activeRequestClientId.current === clientId) {
onClick?.();
}
});
}, [clientId, getClient, onClick]);

return (
<LoadingButton loading={loading} onClick={handleClick} {...buttonProps}>
{children}
</LoadingButton>
);
};

export default ClientForceRefetchButton;
19 changes: 13 additions & 6 deletions src/modules/client/components/ClientProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ import {
} from '@mui/material';
import { useCallback, useRef, useState } from 'react';

import { useNavigate } from 'react-router-dom';
import ClientAddress from './ClientAddress';
import ClientCardImageElement from './ClientCardImageElement';
import ClientContactPoint from './ClientContactPoint';
import ButtonLink from '@/components/elements/ButtonLink';
import ExternalIdDisplay from '@/components/elements/ExternalIdDisplay';
import ClientImageUploadDialog from '@/components/elements/input/ClientImageUploadDialog';
import NotCollectedText from '@/components/elements/NotCollectedText';
import SimpleAccordion from '@/components/elements/SimpleAccordion';
import SimpleTable from '@/components/elements/SimpleTable';
import ClientForceRefetchButton from '@/modules/client/components/ClientForceRefetchButton';
import ClientDobAge from '@/modules/hmis/components/ClientDobAge';
import { ClientSafeSsn } from '@/modules/hmis/components/ClientSsn';
import HmisEnum, { MultiHmisEnum } from '@/modules/hmis/components/HmisEnum';
Expand Down Expand Up @@ -369,6 +370,13 @@ const ClientProfileCard: React.FC<Props> = ({ client }) => {

const size = 175;

const navigate = useNavigate();
const handleOpenClientForm = useCallback(() => {
navigate(
generateSafePath(ClientDashboardRoutes.EDIT, { clientId: client.id })
);
}, [navigate, client.id]);

return (
<Box>
<Card
Expand Down Expand Up @@ -427,18 +435,17 @@ const ClientProfileCard: React.FC<Props> = ({ client }) => {
id={client.id}
permissions='canEditClient'
>
<ButtonLink
<ClientForceRefetchButton
clientId={client.id}
onClick={handleOpenClientForm}
data-testid='editClientButton'
startIcon={<PersonIcon />}
variant='outlined'
color='primary'
fullWidth
to={generateSafePath(ClientDashboardRoutes.EDIT, {
clientId: client.id,
})}
>
Update Client Details
</ButtonLink>
</ClientForceRefetchButton>
</ClientPermissionsFilter>
{client.dateUpdated && (
<Typography
Expand Down
6 changes: 6 additions & 0 deletions src/modules/client/components/pages/ClientDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useIsPrintView from '@/hooks/useIsPrintView';
import useSafeParams from '@/hooks/useSafeParams';
import ClientCardMini from '@/modules/client/components/ClientCardMini';
import ClientPrintHeader from '@/modules/client/components/ClientPrintHeader';
import LocalVersionCoordinationPrompt from '@/modules/localVersionCoordination/LocalVersionCoordinationPrompt';
import { ProjectDashboardContext } from '@/modules/projects/components/ProjectDashboard';
import { ClientFieldsFragment, useGetClientQuery } from '@/types/gqlTypes';

Expand Down Expand Up @@ -82,6 +83,11 @@ const ClientDashboard: React.FC = () => {
{...dashboardState}
>
<Container maxWidth='xl' disableGutters>
<LocalVersionCoordinationPrompt
recordType='Client'
recordId={client.id}
currentVersion={client.lockVersion}
/>
<Outlet context={outletContext} />
</Container>
</DashboardContentContainer>
Expand Down
8 changes: 5 additions & 3 deletions src/modules/enrollment/components/EnrollmentQuickActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button } from '@mui/material';
import { Stack } from '@mui/system';
import ButtonLink from '@/components/elements/ButtonLink';
import TitleCard from '@/components/elements/TitleCard';
import ClientForceRefetchButton from '@/modules/client/components/ClientForceRefetchButton';
import { useClientFormDialog } from '@/modules/client/hooks/useClientFormDialog';
import { DashboardEnrollment } from '@/modules/hmis/types';
import { useHmisAppSettings } from '@/modules/hmisAppSettings/useHmisAppSettings';
Expand Down Expand Up @@ -65,12 +66,13 @@ const EnrollmentQuickActions = ({

{/* Edit Client details */}
{canEditClient && (
<Button
onClick={clientLoading ? undefined : openClientFormDialog}
<ClientForceRefetchButton
clientId={enrollment.client.id}
variant='outlined'
onClick={clientLoading ? undefined : openClientFormDialog}
>
Update Client Details
</Button>
</ClientForceRefetchButton>
)}

{/* View ESG Funding Report */}
Expand Down
11 changes: 11 additions & 0 deletions src/modules/enrollment/components/pages/EnrollmentDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import EnrollmentNavHeader from '@/modules/enrollment/components/EnrollmentNavHe
import { useDetailedEnrollment } from '@/modules/enrollment/hooks/useDetailedEnrollment';
import { useEnrollmentDashboardNavItems } from '@/modules/enrollment/hooks/useEnrollmentDashboardNavItems';
import { DashboardEnrollment } from '@/modules/hmis/types';
import LocalVersionCoordinationPrompt from '@/modules/localVersionCoordination/LocalVersionCoordinationPrompt';
import { ProjectDashboardContext } from '@/modules/projects/components/ProjectDashboard';
import {
DataCollectionFeature,
Expand Down Expand Up @@ -112,6 +113,16 @@ const EnrollmentDashboard: React.FC = () => {
focusMode={focusMode}
>
<Container maxWidth='xl' disableGutters>
<LocalVersionCoordinationPrompt
recordType='Enrollment'
recordId={enrollment.id}
currentVersion={enrollment.lockVersion}
/>
<LocalVersionCoordinationPrompt
recordType='Client'
recordId={client.id}
currentVersion={client.lockVersion}
/>
ttoomey marked this conversation as resolved.
Show resolved Hide resolved
<Outlet context={outletContext} />
</Container>
</DashboardContentContainer>
Expand Down
Loading
Loading