Skip to content

Commit

Permalink
Merge pull request #283 from orppst/274-need-to-be-able-to-delete-a-p…
Browse files Browse the repository at this point in the history
…roposal

Delete proposal in Overview Panel
  • Loading branch information
DJWalker42 authored Nov 26, 2024
2 parents 1038ccb + 597d6cc commit 8176da0
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 78 deletions.
28 changes: 18 additions & 10 deletions src/main/webui/src/App2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
useContext,
ReactElement,
SyntheticEvent,
Context, StrictMode
Context, StrictMode, useReducer
} from 'react';
import {
QueryClient,
Expand Down Expand Up @@ -134,13 +134,17 @@ function App2(): ReactElement {

const GRAY = theme.colors.gray[6];

// hack to force the left-hand navbar to update after proposal deletion in Overview panel
// more precisely, when called, "forceUpdate" will make the entire App rerender - use sparingly!!
const [,forceUpdate] = useReducer(x => x + 1, 0);

// the paths to route to.
const router = createBrowserRouter(
[
{
path: "/manager",
element: <PSTManager />,
errorElement: <ErrorPage />,
errorElement: <ErrorPage />,
children: [
{index: true, element: <PSTManagerStart />},
{
Expand Down Expand Up @@ -203,7 +207,7 @@ function App2(): ReactElement {
{
path: "/",
element: <PSTEditor/>,
errorElement: <ErrorPage />,
errorElement: <ErrorPage />,
children: [
{index: true, element: <PSTStart/>} ,
{
Expand All @@ -218,7 +222,8 @@ function App2(): ReactElement {
},
{
path: "proposal/:selectedProposalCode",
element: <OverviewPanel />,
//'forceUpdate' is called following a proposal deletion in Overview panel
element: <OverviewPanel forceUpdate={forceUpdate}/>,
errorElement: <ErrorPage />,
},
{
Expand Down Expand Up @@ -429,15 +434,18 @@ function App2(): ReactElement {
</Container>

<AddButton toolTipLabel={"new proposal"}
label={"Create a new proposal"}
label={"Create new proposal"}
onClickEvent={handleAddNew}/>
<FileButton onChange={handleUploadZip}
accept={".zip"}>
{(props) => <UploadButton
toolTipLabel="select a file from disk to upload"
label={"Import"}
onClick={props.onClick}/>}
</FileButton>
{(props) =>
<UploadButton
toolTipLabel="select a file from disk to upload"
label={"Import existing proposal"}
onClick={props.onClick}
/>
}
</FileButton>
</AppShell.Section>
<AppShell.Section component={ScrollArea}>
<ProposalListWrapper
Expand Down
183 changes: 125 additions & 58 deletions src/main/webui/src/ProposalEditorView/proposal/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useParams } from 'react-router-dom'
import {useNavigate, useParams} from 'react-router-dom'
import {
fetchProposalResourceDeleteObservingProposal,
useProposalResourceGetObservingProposal,
useSupportingDocumentResourceGetSupportingDocuments,
} from 'src/generated/proposalToolComponents';
import {
Accordion,
Avatar,
Box,
Container,
Container, Fieldset,
Group,
List,
List, Stack,
Table,
Text
} from '@mantine/core';
Expand All @@ -21,13 +22,18 @@ import {
} from 'src/generated/proposalToolSchemas.ts';
import { IconNorthStar } from '@tabler/icons-react';
import { ReactElement, useRef } from 'react';
import { SaveButton } from 'src/commonButtons/save.tsx';
import downloadProposal from './downloadProposal.tsx';
import { DIMMED_FONT_WEIGHT, JSON_SPACES } from 'src/constants.tsx';
import { TargetTable } from '../targets/TargetTable.tsx';
import { TechnicalGoalsTable } from '../technicalGoals/technicalGoalTable.tsx';
import {PreviewJustification} from "../justifications/justification.preview.tsx";
import {ContextualHelpButton} from "../../commonButtons/contextualHelp.tsx"
import {notifyError, notifySuccess} from "../../commonPanel/notifications.tsx";
import getErrorMessage from "../../errorHandling/getErrorMessage.tsx";
import DeleteButton from "../../commonButtons/delete.tsx";
import {PanelFrame, PanelHeader} from "../../commonPanel/appearance.tsx";
import {ExportButton} from "../../commonButtons/export.tsx";
import {modals} from "@mantine/modals";

/*
title -- string
Expand Down Expand Up @@ -219,9 +225,11 @@ function ObservationAccordionContent(
* @return {ReactElement} the html of the overview panel.
* @constructor
*/
function OverviewPanel(): ReactElement {
function OverviewPanel(props: {forceUpdate: () => void}): ReactElement {
const { selectedProposalCode } = useParams();

const navigate = useNavigate();

const {data} =
useSupportingDocumentResourceGetSupportingDocuments(
{pathParams: {proposalCode: Number(selectedProposalCode)},},
Expand All @@ -245,36 +253,6 @@ function OverviewPanel(): ReactElement {
);
}

/**
* generates the overview pdf and saves it to the users disk.
*
* code extracted from: https://www.robinwieruch.de/react-component-to-pdf/
* @return {Promise<void>} promise that the pdf will be saved at some point.
*/
const handleDownloadPdf = (): void => {
// get the overview page to print as well as the proposal data.
const element = printRef.current;

// ensure there is a rendered overview.
if(element !== null && proposalsData !== undefined &&
selectedProposalCode !== undefined && data !== undefined) {
downloadProposal(
element, proposalsData, data, selectedProposalCode).then();
} else {
// something failed in the rendering of the overview react element or
// extracting the proposal data.
if (element === null) {
console.error(
'Tried to download a Overview that had not formed ' +
'correctly.');
} else {
console.error(
'Tried to download the proposal data and that had not ' +
'formed correctly.');
}
}
};

/**
* handles the title display panel.
* @return {ReactElement} the html for the title display panel.
Expand Down Expand Up @@ -534,48 +512,137 @@ function OverviewPanel(): ReactElement {
)
}


/**
* generates the overview pdf and saves it to the users disk.
*
* code extracted from: https://www.robinwieruch.de/react-component-to-pdf/
* @return {Promise<void>} promise that the pdf will be saved at some point.
*/
const handleDownloadPdf = (): void => {
// get the overview page to print as well as the proposal data.
const element = printRef.current;

// ensure there is a rendered overview.
if(element !== null && proposalsData !== undefined &&
selectedProposalCode !== undefined && data !== undefined) {
downloadProposal(
element, proposalsData, data, selectedProposalCode).then();
} else {
// something failed in the rendering of the overview react element or
// extracting the proposal data.
if (element === null) {
console.error(
'Tried to download a Overview that had not formed ' +
'correctly.');
} else {
console.error(
'Tried to download the proposal data and that had not ' +
'formed correctly.');
}
}
};


/**
* add download button for the proposal to be extracted as a tar ball.
*
* @return {ReactElement} the html which contains the download button.
* @constructor
*/
const DownloadButton = (): ReactElement => {
return SaveButton(
const ExportProposal = (): ReactElement => {
return ExportButton(
{
toolTipLabel: `Export to a file for download`,
disabled: false,
onClick: handleDownloadPdf,
label: "Export",
label: "Export Proposal",
variant: "filled",
toolTipLabelPosition: "top"
});
}

const DeleteProposal = () : ReactElement => {
return DeleteButton(
{
toolTipLabel: "Removes this proposal permanently",
disabled: false,
onClick: confirmDeleteProposal,
label: "Delete Proposal",
variant: "outline"
}
)
}


const confirmDeleteProposal = () : void => {
modals.openConfirmModal({
title: "Confirm Proposal Deletion",
centered: true,
children: (
<Stack>
<Text size={"sm"}>
Are you sure you want to permanently remove the proposal '{proposalsData?.title!}'?
</Text>
<Text size={"sm"} c={"yellow.7"}>
This action cannot be undone.
</Text>
</Stack>

),
labels: {confirm: "Yes, delete this proposal", cancel: "No, do not delete!"},
confirmProps: {color: "red"},
onConfirm: () => handleDeleteProposal()
})
}


const handleDeleteProposal = () => {
fetchProposalResourceDeleteObservingProposal({
pathParams: {proposalCode: Number(selectedProposalCode)}
})
.then(()=> notifySuccess(
"Deletion successful",
"Proposal: '" + proposalsData?.title! + "' has been removed"))
.then(()=> navigate("/"))
.then(() => props.forceUpdate())
.catch(error => notifyError("Deletion failed", getErrorMessage(error)))
}


/**
* returns the HTML structure for the overview page.
*/
return (
<>
{
proposalsIsLoading ? 'Loading...' :
<Container fluid>
<DownloadButton/>
<div ref={printRef}>
<DisplayTitle/>
<ContextualHelpButton messageId="Overview" />
<DisplayInvestigators/>
<DisplaySummary/>
<DisplayKind/>
<DisplayScientificJustification/>
<DisplayTechnicalJustification/>
<DisplayObservations/>
<DisplaySupportingDocuments/>
<DisplayRelatedProposals/>
</div>
</Container>
}
</>
<PanelFrame>
<PanelHeader
itemName={proposalsData?.title!}
panelHeading={"Overview"}
isLoading={proposalsIsLoading}
/>
<Container fluid>
<ContextualHelpButton messageId="Overview" />
<Fieldset legend={"Proposal Services"}>
<Group grow>
<ExportProposal/>
<DeleteProposal/>
</Group>
</Fieldset>
<Fieldset legend={"Proposal Overview"}>
<div ref={printRef}>
<DisplayTitle/>
<DisplayInvestigators/>
<DisplaySummary/>
<DisplayKind/>
<DisplayScientificJustification/>
<DisplayTechnicalJustification/>
<DisplayObservations/>
<DisplaySupportingDocuments/>
<DisplayRelatedProposals/>
</div>
</Fieldset>
</Container>
</PanelFrame>
);

}
Expand Down
2 changes: 1 addition & 1 deletion src/main/webui/src/commonButtons/delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function DeleteButton(props: ClickButtonInterfaceProps): ReactElement {
>
<Button
rightSection={<IconTrash size={ICON_SIZE}/>}
color={"red.5"}
color={"red.7"}
variant={props.variant ?? "subtle"}
onClick={props.onClick ?? props.onClickEvent}
disabled={props.disabled}
Expand Down
25 changes: 25 additions & 0 deletions src/main/webui/src/commonButtons/export.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ClickButtonInterfaceProps} from "./buttonInterfaceProps.tsx";
import {ReactElement} from "react";
import {Button, Tooltip} from "@mantine/core";
import {CLOSE_DELAY, ICON_SIZE, OPEN_DELAY} from "../constants.tsx";
import {IconDownload} from "@tabler/icons-react";

export
function ExportButton(props: ClickButtonInterfaceProps) : ReactElement {
return (
<Tooltip position={props.toolTipLabelPosition ?? "left"}
label={props.toolTipLabel}
openDelay={OPEN_DELAY}
closeDelay={CLOSE_DELAY}
>
<Button
rightSection={<IconDownload size={ICON_SIZE}/>}
color={"blue.7"}
variant={props.variant ?? "subtle"}
onClick={props.onClick === undefined? props.onClickEvent : props.onClick}
disabled={props.disabled}>
{props.label === undefined? 'Export' : props.label}
</Button>
</Tooltip>
)
}
18 changes: 9 additions & 9 deletions src/main/webui/src/commonButtons/save.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,17 @@ function SubmitButton(props: BasicButtonInterfaceProps): ReactElement {
*/
export function SaveButton(props: ClickButtonInterfaceProps): ReactElement {
return (
<Tooltip position={"left"}
<Tooltip position={props.toolTipLabelPosition ?? "left"}
label={props.toolTipLabel}
openDelay={OPEN_DELAY}
closeDelay={CLOSE_DELAY}>
<Button rightSection={<IconDeviceFloppy size={ICON_SIZE}/>}
color={"violet.5"}
variant={"subtle"}
onClick={props.onClick === undefined?
props.onClickEvent :
props.onClick}
disabled={props.disabled}>
closeDelay={CLOSE_DELAY}
>
<Button
rightSection={<IconDeviceFloppy size={ICON_SIZE}/>}
color={"blue.7"}
variant={props.variant ?? "subtle"}
onClick={props.onClick === undefined? props.onClickEvent : props.onClick}
disabled={props.disabled}>
{props.label === undefined? 'Save' : props.label}
</Button>
</Tooltip>
Expand Down

0 comments on commit 8176da0

Please sign in to comment.