Skip to content

Commit

Permalink
feat: add manage reports page
Browse files Browse the repository at this point in the history
  • Loading branch information
RRanath authored and ccbc-service-account committed Dec 24, 2024
1 parent ac40a5a commit b4cbfbc
Show file tree
Hide file tree
Showing 6 changed files with 510 additions and 0 deletions.
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

0 comments on commit b4cbfbc

Please sign in to comment.