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

button in note cell to add a note #325

Merged
merged 2 commits into from
Oct 5, 2023
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
11 changes: 9 additions & 2 deletions cypress/e2e/Search/search.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ describe('Search Incidents', { failFast: { enabled: false } }, () => {
cy.get('#global-search-input').clear().type('foobar');
cy.wait(1000);
cy.get('[data-incident-header="Latest Note"]').each(($el) => {
cy.wrap($el).should('have.text', 'foobar');
// cy.wrap($el).should('have.text', 'foobar');
cy.wrap($el).find('*').should((subElements) => {
const elementWithFoobar = subElements.toArray().find((el) => el.textContent.includes('foobar'));
assert.isNotNull(elementWithFoobar, 'Expected to find a subelement containing "foobar"');
});
});
});
cy.get('#global-search-input').clear();
Expand All @@ -91,7 +95,10 @@ describe('Search Incidents', { failFast: { enabled: false } }, () => {
cy.get('#global-search-input').clear().type('foobaz');
cy.wait(1000);
cy.get('[data-incident-header="Latest Note"]').each(($el) => {
cy.wrap($el).should('have.text', 'foobar');
cy.wrap($el).find('*').should((subElements) => {
const elementWithFoobar = subElements.toArray().find((el) => el.textContent.includes('foobar'));
assert.isNotNull(elementWithFoobar, 'Expected to find a subelement containing "foobar"');
});
});
});
cy.get('#global-search-input').clear();
Expand Down
129 changes: 129 additions & 0 deletions src/components/IncidentTable/subcomponents/LatestNoteComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, {
useRef,
useState,
} from 'react';

import {
useDispatch,
} from 'react-redux';

import {
Box,
Flex,
IconButton,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
PopoverArrow,
PopoverCloseButton,
Skeleton,
PopoverHeader,
Textarea,
useDisclosure,
} from '@chakra-ui/react';

import {
AddIcon,
CheckIcon,
} from '@chakra-ui/icons';

import i18next from 'i18next';

import Linkify from 'linkify-react';

import {
addNote as addNoteConnected,
} from 'src/redux/incident_actions/actions';

const linkifyOptions = { target: { url: '_blank' }, rel: 'noopener noreferrer' };

const LatestNoteComponent = ({
incident,
}) => {
const noteRef = useRef(null);
const [note, setNote] = useState('');
const {
isOpen,
onToggle,
onClose,
} = useDisclosure();
const dispatch = useDispatch();
const addNote = (incidents, noteContent) => dispatch(addNoteConnected(incidents, noteContent, true, false));

return (
<Box
position="relative"
width="full"
display="flex"
alignItems="center"
justifyContent="space-between"
>
<Box
className="td-wrapper"
pr={8}
>
<Linkify options={linkifyOptions}>
{incident.notes?.length > 0 && incident.notes.slice(-1)[0].content}
{incident.notes?.length === 0 && '--'}
{incident.notes?.status === 'fetching' && <Skeleton>fetching</Skeleton>}
</Linkify>
</Box>
<Popover
isOpen={isOpen}
onClose={onClose}
initialFocusRef={noteRef}
>
<PopoverTrigger>
<IconButton
size="xs"
position="absolute"
right={0}
top="50%"
transform="translateY(-50%)"
colorScheme="blue"
icon={<AddIcon />}
onClick={() => {
onToggle();
}}
aria-label={i18next.t('Add Note')}
/>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverCloseButton />
<PopoverHeader>
{i18next.t('Add Note')}
</PopoverHeader>
<PopoverBody>
<Flex>
<Textarea
size="sm"
resize="none"
ref={noteRef}
value={note}
onChange={(e) => {
setNote(e.target.value);
}}
/>
<IconButton
size="sm"
ml={2}
onClick={() => {
addNote([incident], note);
setNote('');
onClose();
}}
isDisabled={!note}
colorScheme="blue"
icon={<CheckIcon />}
/>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
</Box>
);
};

export default LatestNoteComponent;
12 changes: 5 additions & 7 deletions src/config/column-generator.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import NumAlertsComponent from 'src/components/IncidentTable/subcomponents/NumAl
import LinksComponent from 'src/components/IncidentTable/subcomponents/LinksComponent';
import PersonInitialsComponents from 'src/components/IncidentTable/subcomponents/PersonInitialsComponents';
import ColumnFilterComponent from 'src/components/IncidentTable/subcomponents/ColumnFilterComponent';
import LatestNoteComponent from 'src/components/IncidentTable/subcomponents/LatestNoteComponent';

const linkifyOptions = { target: { url: '_blank' }, rel: 'noopener noreferrer' };

Expand Down Expand Up @@ -496,13 +497,9 @@ export const defaultIncidentColumns = () => [
original,
},
}) => (
<CellDiv>
<Linkify options={linkifyOptions}>
{original.notes?.length > 0 && original.notes.slice(-1)[0].content}
{original.notes?.length === 0 && '--'}
{original.notes?.status === 'fetching' && <Skeleton>fetching</Skeleton>}
</Linkify>
</CellDiv>
<LatestNoteComponent
incident={original}
/>
),
}),
incidentColumn({
Expand Down Expand Up @@ -840,6 +837,7 @@ export const incidentColumnsTranslations = [
i18next.t('Incident ID'),
i18next.t('Summary'),
i18next.t('Latest Note'),
i18next.t('Add Note'),
i18next.t('Latest Note At'),
i18next.t('External References'),
i18next.t('Responders'),
Expand Down
3 changes: 2 additions & 1 deletion src/redux/incident_actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,12 @@ export const updatePriority = (incidents, priorityId, displayModal = true) => ({
displayModal,
});

export const addNote = (incidents, note, displayModal = true) => ({
export const addNote = (incidents, note, displayModal = true, toggleModal = true) => ({
type: ADD_NOTE_REQUESTED,
incidents,
note,
displayModal,
toggleModal,
});

export const toggleDisplayAddNoteModal = () => ({
Expand Down
4 changes: 2 additions & 2 deletions src/redux/incident_actions/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ export function* addNoteAsync() {
export function* addNote(action) {
try {
const {
incidents: selectedIncidents, note, displayModal,
incidents: selectedIncidents, note, displayModal, toggleModal,
} = action;

// Build individual requests as the endpoint supports singular POST
Expand All @@ -742,7 +742,7 @@ export function* addNote(action) {
// Invoke parallel calls for optimal performance
const responses = yield all(addNoteRequests);
if (responses.every((response) => response.status >= 200 && response.status < 300)) {
yield toggleDisplayAddNoteModalImpl();
if (toggleModal) yield toggleDisplayAddNoteModalImpl();
if (displayModal) {
const actionAlertsModalType = 'success';
const actionAlertsModalMessage = `${i18next.t('Incident')}(s) ${selectedIncidents
Expand Down