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

feat: add manage reports page #3746

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
108 changes: 108 additions & 0 deletions app/components/Reporting/ManageReports.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import styled from 'styled-components';
import { useToast } from 'components/AppProvider';
import { useState } from 'react';
import { DateTime } from 'luxon';
import { useArchiveReportingGcpeMutation } from 'schema/mutations/reporting/archiveReportingGcpeMutation';
import { ConnectionHandler } from 'relay-runtime';
import * as Sentry from '@sentry/nextjs';
import ReportRow from './ReportRow';

const StyledH2 = styled.h2`
margin-top: 12px;
`;

const ManageReports = ({ reportList, connectionId }) => {
const { showToast, hideToast } = useToast();
const [updateReportingGcpeByRowId] = useArchiveReportingGcpeMutation();

const [isLoading, setIsLoading] = useState(false);

const handleArchive = (report) => {
const { __id: reportConnectionId, rowId, createdAt } = report;
const formattedFileName = `Generated ${DateTime.fromISO(createdAt)
.setZone('America/Los_Angeles')
.toLocaleString(DateTime.DATETIME_FULL)}`;
hideToast();
setIsLoading(true);
const variables = {
input: {
reportingGcpePatch: {
archivedAt: new Date().toISOString(),
},
rowId,
},
};
updateReportingGcpeByRowId({
variables,
updater: (store) => {
const connection = store.get(connectionId);
store.delete(reportConnectionId);
ConnectionHandler.deleteNode(connection, reportConnectionId);
},
onError: (response) => {
setIsLoading(false);
showToast(
'Error archiving GCPE file. Please try again later.',
'error',
5000
);
Sentry.captureException({
name: `Error archiving GCPE file: ${formattedFileName}`,
message: response,
});
},
onCompleted: () => {
setIsLoading(false);
showToast('GCPE file archived successfully', 'success', 5000);
},
});
};

const handleDownload = async (report: any) => {
const { rowId, createdAt } = report;
await fetch('/api/reporting/gcpe/regenerate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ rowId }),
})
.then((response) => {
response.blob().then((blob) => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${DateTime.fromISO(createdAt)
.setZone('America/Los_Angeles')
.toLocaleString(
DateTime.DATETIME_FULL
)}_Connectivity_Projects_GCPE_List.xlsx`;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
});
})
.catch((error) => {
showToast('Error downloading file. Please try again later.', error);
});
};

return (
<>
<StyledH2>Manage My Reports</StyledH2>
{reportList.map((edge) => (
<ReportRow
key={edge.node.id}
report={edge.node}
onDownload={() => handleDownload(edge.node)}
onArchive={() => handleArchive(edge.node)}
isLoading={isLoading}
reportType="GCPE"
/>
))}
</>
);
};

export default ManageReports;
87 changes: 87 additions & 0 deletions app/components/Reporting/ReportRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import useModal from 'lib/helpers/useModal';
import GenericConfirmationModal from 'lib/theme/widgets/GenericConfirmationModal';
import { DateTime } from 'luxon';
import styled from 'styled-components';

const StyledFileDiv = styled('div')`
display: flex;
flex-direction: row;
align-items: center;
word-break: break-word;
margin-left: 16px;
margin-top: 10px;
& svg {
margin: 0px 8px;
}
`;

const StyledDeleteBtn = styled('button')`
&:hover {
opacity: 0.6;
}
`;

const StyledLink = styled.button`
width: 380px;
color: ${(props) => props.theme.color.links};
text-decoration-line: underline;
text-align: left;
`;

const ReportRow = ({
report,
onDownload,
onArchive,
isLoading,
reportType,
}) => {
const { id, createdAt } = report;

const deleteConfirmationModal = useModal();

const formattedFileName = `Generated ${DateTime.fromISO(createdAt)
.setZone('America/Los_Angeles')
.toLocaleString(DateTime.DATETIME_FULL)}`;

return (
<>
<StyledFileDiv key={id}>
<StyledLink
data-testid="file-download-link"
onClick={(e) => {
e.preventDefault();
onDownload();
}}
>
{formattedFileName}
</StyledLink>
<StyledDeleteBtn
data-testid="file-delete-btn"
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
deleteConfirmationModal.open();
}}
disabled={isLoading}
>
<FontAwesomeIcon width={10} icon={faTrash} color="rgb(189, 36, 36)" />
</StyledDeleteBtn>
</StyledFileDiv>
<GenericConfirmationModal
id="report-delete-confirm-modal"
title="Delete"
message={`Are you sure you want to delete this ${reportType} file: ${formattedFileName}?`}
okLabel="Delete"
cancelLabel="Cancel"
onConfirm={() => {
onArchive();
deleteConfirmationModal.close();
}}
{...deleteConfirmationModal}
/>
</>
);
};

export default ReportRow;
7 changes: 7 additions & 0 deletions app/components/Reporting/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ const StyledNav = styled.nav`
const Tabs = () => {
const router = useRouter();
const gcpeHref = '/analyst/reporting/gcpe';
const manageReportsHref = '/analyst/reporting/manage-reports';
// this a bare page to handle any future reporting tabs
return (
<StyledNav>
<StyledTab href={gcpeHref} selected={router?.pathname.includes(gcpeHref)}>
GCPE
</StyledTab>
<StyledTab
href={manageReportsHref}
selected={router?.pathname.includes(manageReportsHref)}
>
Manage Reports
</StyledTab>
</StyledNav>
);
};
Expand Down
79 changes: 79 additions & 0 deletions app/pages/analyst/reporting/manage-reports.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Layout } from 'components';
import { DashboardTabs } from 'components/AnalystDashboard';
import { graphql } from 'react-relay';
import { usePreloadedQuery } from 'react-relay/hooks';
import { withRelay, RelayProps } from 'relay-nextjs';
import styled from 'styled-components';
import defaultRelayOptions from 'lib/relay/withRelayOptions';
import Tabs from 'components/Reporting/Tabs';
import ManageReports from 'components/Reporting/ManageReports';
import { manageReportsQuery } from '__generated__/manageReportsQuery.graphql';

const getManageReportingQuery = graphql`
query manageReportsQuery {
allReportingGcpes(
first: 9999
condition: { archivedAt: null }
orderBy: ID_DESC
) @connection(key: "ManageReporting_allReportingGcpes") {
__id
edges {
node {
__id
id
rowId
createdAt
createdBy
ccbcUserByCreatedBy {
id
sessionSub
}
}
}
}
session {
sub
...DashboardTabs_query
}
}
`;

const StyledContainer = styled.div`
width: 100%;
height: 100%;
`;

const ManageReporting = ({
preloadedQuery,
}: RelayProps<Record<string, unknown>, manageReportsQuery>) => {
const query = usePreloadedQuery(getManageReportingQuery, preloadedQuery);
const { allReportingGcpes, session } = query;

const reportList =
allReportingGcpes &&
[...allReportingGcpes.edges].filter((data: any) => {
return (
data.node !== null &&
data.node?.ccbcUserByCreatedBy.sessionSub === session?.sub
);
});

return (
<Layout session={session} title="Connecting Communities BC">
<StyledContainer>
<DashboardTabs session={session} />
<Tabs />
<ManageReports
reportList={reportList}
connectionId={allReportingGcpes.__id}
/>
</StyledContainer>
</Layout>
);
};

export default withRelay(
ManageReporting,
getManageReportingQuery,
defaultRelayOptions
);
25 changes: 25 additions & 0 deletions app/schema/mutations/reporting/archiveReportingGcpeMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { graphql } from 'react-relay';
import { archiveReportingGcpeMutation } from '__generated__/archiveReportingGcpeMutation.graphql';
import useMutationWithErrorMessage from '../useMutationWithErrorMessage';

const mutation = graphql`
mutation archiveReportingGcpeMutation(
$input: UpdateReportingGcpeByRowIdInput!
) {
updateReportingGcpeByRowId(input: $input) {
reportingGcpe {
id
archivedAt
createdAt
}
}
}
`;

const useArchiveReportingGcpeMutation = () =>
useMutationWithErrorMessage<archiveReportingGcpeMutation>(
mutation,
() => 'An error occured while attempting to archive the intake'
);

export { mutation, useArchiveReportingGcpeMutation };
Loading
Loading