Skip to content

Commit

Permalink
Merge pull request #605 from psarando/CORE-1955-app-ver-reorder
Browse files Browse the repository at this point in the history
CORE-1955 App Version Ordering Form
  • Loading branch information
psarando authored Nov 21, 2024
2 parents 6e9cb1b + 89fe48e commit 51b2bb4
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 3 deletions.
2 changes: 2 additions & 0 deletions public/static/locales/en/app_editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"newParamLabel": "New {{type}}",
"newSectionLabel": "New Section",
"noToolWarning": "The app will not be able to run since a tool has not been selected.",
"orderAppVersions": "Order App Versions",
"orderAppVersionsHelp": "Order versions with the newest (latest) at the top, and the oldest at the bottom.",
"outputCategoryTitle": "Output",
"parameters": "Parameters",
"parameterLabel": "Parameter Label",
Expand Down
1 change: 1 addition & 0 deletions public/static/locales/en/apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"done": "Done",
"edit": "Edit",
"editApp": "Edit App",
"editAppVersionOrder": "Edit Version Order",
"editWorkflow": "Edit Workflow",
"embedInstantLaunchNotUsed": "This embed code will send users to the app launch page. If you would prefer it launch the app immediately, turn on the \"Use Instant Launch\" switch.",
"embedInstantLaunchUsed": "This embed code will cause users to launch the app immediately. If you would prefer they be sent to the app launch page, turn off the \"Use Instant Launch\" switch.",
Expand Down
204 changes: 204 additions & 0 deletions src/components/apps/editor/VersionsOrderingForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/**
* A form component for setting App version order.
*
* @author psarando
*/
import React from "react";

import { FieldArray, Form, Formik } from "formik";
import { useMutation } from "react-query";

import { useTranslation } from "i18n";

import ids from "./ids";
import styles from "./styles";

import { announce } from "components/announcer/CyVerseAnnouncer";
import { SUCCESS } from "components/announcer/AnnouncerConstants";
import withErrorAnnouncer from "components/error/withErrorAnnouncer";
import WrappedErrorHandler from "components/error/WrappedErrorHandler";
import TableLoading from "components/table/TableLoading";
import buildID from "components/utils/DebugIDUtil";
import BackButton from "components/utils/BackButton";

import { setAppVersionOrder } from "serviceFacades/apps";

import {
Button,
ButtonGroup,
Card,
CardContent,
CardHeader,
Table,
Toolbar,
Typography,
} from "@mui/material";

import { makeStyles } from "tss-react/mui";

import { ArrowDownward, ArrowUpward } from "@mui/icons-material";

const useStyles = makeStyles()(styles);

function VersionOrderForm(props) {
const { baseId, version, onMoveUp, onMoveDown } = props;

const { t } = useTranslation("common");
const { classes } = useStyles();

const versionBaseId = buildID(baseId, version.version_id);

return (
<Card className={classes.paramCard}>
<CardHeader
title={version.version}
titleTypographyProps={{ variant: "body1" }}
action={
<ButtonGroup color="primary" variant="text">
<Button
id={buildID(versionBaseId, ids.BUTTONS.MOVE_UP_BTN)}
aria-label={t("moveUp")}
onClick={onMoveUp}
>
<ArrowUpward />
</Button>
<Button
id={buildID(
versionBaseId,
ids.BUTTONS.MOVE_DOWN_BTN
)}
aria-label={t("moveDown")}
onClick={onMoveDown}
>
<ArrowDownward />
</Button>
</ButtonGroup>
}
/>
</Card>
);
}

function VersionsOrderingForm(props) {
const { baseId, app, isLoading, error, showErrorAnnouncer } = props;
const { system_id: systemId, id: appId, versions } = app || {};

const { t } = useTranslation(["app_editor", "common"]);

const { mutate: saveAppVersions } = useMutation(
({ values }) =>
setAppVersionOrder({ systemId, appId, versions: values }),
{
onSuccess: (resp, { onSuccess }) => {
onSuccess(resp);
},
onError: (error, { onError }) => {
onError(error);
},
}
);

return error ? (
<WrappedErrorHandler errorObject={error} baseId={baseId} />
) : isLoading ? (
<Table>
<TableLoading baseId={baseId} numColumns={2} numRows={3} />
</Table>
) : (
<Formik
initialValues={{ versions }}
onSubmit={(values, actions) => {
const { resetForm, setSubmitting, setStatus } = actions;

const onSuccess = ({ versions }) => {
setSubmitting(false);
setStatus({ success: true });
resetForm({ values: { versions } });

announce({
text: t("appSaved"),
variant: SUCCESS,
});
};

const onError = (errorMessage) => {
showErrorAnnouncer(t("appSaveErr"), errorMessage);

setSubmitting(false);
setStatus({ success: false, errorMessage });
};

saveAppVersions({ values, onSuccess, onError });
}}
>
{({ handleSubmit, isSubmitting, dirty, values }) => {
return (
<Form>
<Toolbar variant="dense">
<BackButton dirty={dirty} />
<Typography
id={buildID(baseId, ids.TITLE)}
variant="h6"
paddingLeft={1}
style={{ flex: 1, overflow: "auto" }}
>
{t("orderAppVersions")}
</Typography>
<Button
id={buildID(baseId, ids.BUTTONS.SAVE_BTN)}
variant="contained"
type="submit"
onClick={handleSubmit}
disabled={!dirty || isSubmitting}
>
{t("common:save")}
</Button>
</Toolbar>

<Card>
<CardHeader title={t("orderAppVersionsHelp")} />
<CardContent>
<FieldArray
name="versions"
render={(arrayHelpers) =>
values.versions.map(
(version, index) => (
<VersionOrderForm
key={version.version_id}
baseId={baseId}
version={version}
onMoveUp={() => {
if (index > 0) {
arrayHelpers.move(
index,
index - 1
);
}
}}
onMoveDown={() => {
if (
index + 1 <
values.versions
.length
) {
arrayHelpers.move(
index,
index + 1
);
}
}}
/>
)
)
}
/>
</CardContent>
</Card>
</Form>
);
}}
</Formik>
);
}

export default withErrorAnnouncer(VersionsOrderingForm);
1 change: 1 addition & 0 deletions src/components/apps/ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default {
CREATE_APP_VERSION_MENU_ITEM: "createAppVersionMenuItem",
CREATE_MENU_ITEM: "createMenuItem",
CREATE_WORKFLOW_MENU_ITEM: "createWorkflowMenuItem",
EDIT_APP_VERSION_ORDER_MENU_ITEM: "editAppVersionOrderMenuItem",
DIALOG: "dialog",
APP_SAVED_LAUNCH: "appSavedLaunch",
LISTING_TABLE: "listingTable",
Expand Down
15 changes: 12 additions & 3 deletions src/components/apps/listing/RowDotMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import utilIds from "components/utils/ids";
import { getAppListingLinkRefs } from "components/apps/utils";
import { useUserProfile } from "contexts/userProfile";
import PublishAppDialog from "../PublishAppDialog";
import EditVersionOrderMenuItem from "../menuItems/EditVersionOrderMenuItem";

function RowDotMenu(props) {
const {
Expand Down Expand Up @@ -78,6 +79,14 @@ function RowDotMenu(props) {
onClose={onClose}
onDetailsSelected={onDetailsSelected}
/>,
!isAdminView && canEditLabels && (
<EditMenuItem
key={buildID(baseId, ids.EDIT_MENU_ITEM)}
baseId={baseId}
onClose={onClose}
app={app}
/>
),
canEdit && (
<CreateVersionMenuItem
key={ids.CREATE_APP_VERSION_MENU_ITEM}
Expand All @@ -86,9 +95,9 @@ function RowDotMenu(props) {
app={app}
/>
),
!isAdminView && canEditLabels && (
<EditMenuItem
key={buildID(baseId, ids.EDIT_MENU_ITEM)}
canEdit && (
<EditVersionOrderMenuItem
key={ids.EDIT_APP_VERSION_ORDER_MENU_ITEM}
baseId={baseId}
onClose={onClose}
app={app}
Expand Down
36 changes: 36 additions & 0 deletions src/components/apps/menuItems/EditVersionOrderMenuItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";

import { useRouter } from "next/router";

import { useTranslation } from "i18n";
import ids from "../ids";

import NavigationConstants from "common/NavigationConstants";
import buildID from "components/utils/DebugIDUtil";

import { ListItemIcon, ListItemText, MenuItem } from "@mui/material";
import { Edit } from "@mui/icons-material";

export default function EditVersionOrderMenuItem(props) {
const { baseId, app, onClose } = props;

const router = useRouter();
const { t } = useTranslation("apps");

return (
<MenuItem
id={buildID(baseId, ids.EDIT_APP_VERSION_ORDER_MENU_ITEM)}
onClick={() => {
onClose();
router.push(
`/${NavigationConstants.APPS}/${app.system_id}/${app.id}/versions`
);
}}
>
<ListItemIcon>
<Edit fontSize="small" />
</ListItemIcon>
<ListItemText primary={t("editAppVersionOrder")} />
</MenuItem>
);
}
57 changes: 57 additions & 0 deletions src/pages/apps/[systemId]/[appId]/versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* A page for displaying the App Versions Ordering Form.
*
* @author psarando
*/
import React from "react";

import { useRouter } from "next/router";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { useQuery } from "react-query";

import { i18n, RequiredNamespaces } from "i18n";

import ids from "components/apps/editor/ids";
import VersionsOrderingForm from "components/apps/editor/VersionsOrderingForm";

import {
getAppDescription,
APP_DESCRIPTION_QUERY_KEY,
} from "serviceFacades/apps";

export default function VersionsOrderEdit() {
const [app, setApp] = React.useState(null);

const router = useRouter();
const { systemId, appId } = router.query;

const { isFetching, error } = useQuery({
queryKey: [APP_DESCRIPTION_QUERY_KEY, { systemId, appId }],
queryFn: () => getAppDescription({ systemId, appId }),
enabled: !!(systemId && appId),
onSuccess: setApp,
});

return (
<VersionsOrderingForm
baseId={ids.APP_VERSION}
app={app}
error={error}
isLoading={isFetching}
/>
);
}

export async function getServerSideProps({ locale }) {
const title = i18n.t("app_editor:orderAppVersions");

return {
props: {
title,
...(await serverSideTranslations(locale, [
"app_editor",
...RequiredNamespaces,
])),
},
};
}
13 changes: 13 additions & 0 deletions src/server/api/apps.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ export default function appsRouter() {
})
);

logger.info("adding the PUT /apps/:systemId/:appId/versions handler");
api.put(
"/apps/:systemId/:appId/versions",
auth.authnTokenMiddleware,
terrainHandler({
method: "PUT",
pathname: "/apps/:systemId/:appId/versions",
headers: {
"Content-Type": "application/json",
},
})
);

logger.info(
"adding the GET /apps/:systemId/:appId/versions/:versionId handler"
);
Expand Down
Loading

0 comments on commit 51b2bb4

Please sign in to comment.