diff --git a/public/static/locales/en/common.json b/public/static/locales/en/common.json
index 14c8289c8..8c31ce83e 100644
--- a/public/static/locales/en/common.json
+++ b/public/static/locales/en/common.json
@@ -43,6 +43,7 @@
"help": "Help",
"home": "Home",
"instantLaunches": "Instant Launches",
+ "instantLaunch": "Instant Launch",
"interactiveAnalysisUrl": "{{message}}. Access your running analysis here.",
"intercomAriaLabel": "Chat with CyVerse support",
"learnMore": "Learn More",
diff --git a/src/components/instantlaunches/InstantLaunchButtonWrapper.js b/src/components/instantlaunches/InstantLaunchButtonWrapper.js
index 423b09a8b..3886f7636 100644
--- a/src/components/instantlaunches/InstantLaunchButtonWrapper.js
+++ b/src/components/instantlaunches/InstantLaunchButtonWrapper.js
@@ -8,7 +8,7 @@
* The `onClick` function will handle launching the instant launch and
* briefly displaying the Submission dialog to the user.
*/
-import React from "react";
+import React, { useEffect, useCallback } from "react";
import { useMutation } from "react-query";
@@ -36,12 +36,14 @@ function InstantLaunchButtonWrapper(props) {
resource = {},
render,
showErrorAnnouncer,
+ autolaunch,
} = props;
const output_dir = useDefaultOutputDir();
const [userProfile] = useUserProfile();
const [bootstrapInfo] = useBootstrapInfo();
const [open, setOpen] = React.useState(false);
+ const [hasLaunched, setHasLaunched] = React.useState(false);
const [signInDlgOpen, setSignInDlgOpen] = React.useState(false);
const [accessRequestDialogOpen, setAccessRequestDialogOpen] =
React.useState(false);
@@ -101,8 +103,11 @@ function InstantLaunchButtonWrapper(props) {
},
});
- const onClick = () => {
- if (userProfile?.id) {
+ const preferences = bootstrapInfo?.preferences;
+ const userId = userProfile?.id;
+
+ const onClick = useCallback(() => {
+ if (userId) {
if (computeLimitExceeded) {
showErrorAnnouncer(t("computeLimitExceededMsg"));
} else {
@@ -111,17 +116,34 @@ function InstantLaunchButtonWrapper(props) {
instantLaunch,
resource,
output_dir,
- preferences: bootstrapInfo?.preferences,
+ preferences,
});
}
} else {
setSignInDlgOpen(true);
}
- };
+ }, [
+ preferences,
+ computeLimitExceeded,
+ instantLaunch,
+ launch,
+ output_dir,
+ resource,
+ showErrorAnnouncer,
+ t,
+ userId,
+ ]);
+
+ useEffect(() => {
+ if (autolaunch && !hasLaunched) {
+ onClick();
+ setHasLaunched(true);
+ }
+ }, [autolaunch, onClick, hasLaunched, setHasLaunched]);
return (
<>
- {render(onClick)}
+ {!autolaunch && render && render(onClick)}
{
+ const { id: instant_launch_id, showErrorAnnouncer } = props;
+ const [config] = useConfig();
+ const [userProfile] = useUserProfile();
+ const [computeLimitExceeded, setComputeLimitExceeded] = useState(
+ !!config?.subscriptions?.enforce
+ );
+ const [errorDialogOpen, setErrorDialogOpen] = useState(false);
+ const { t } = useTranslation(["instantlaunches", "common"]);
+
+ const { data, status, error } = useQuery(
+ [GET_INSTANT_LAUNCH_FULL_KEY, instant_launch_id],
+ () => getFullInstantLaunch(instant_launch_id)
+ );
+
+ const { isFetching: isFetchingUsageSummary } = useQuery({
+ queryKey: [RESOURCE_USAGE_QUERY_KEY],
+ queryFn: getResourceUsageSummary,
+ enabled: !!config?.subscriptions?.enforce && !!userProfile?.id,
+ onSuccess: (respData) => {
+ const computeUsage = respData?.cpu_usage?.total || 0;
+ const subscription = respData?.subscription;
+ const computeQuota = getUserQuota(
+ globalConstants.CPU_HOURS_RESOURCE_NAME,
+ subscription
+ );
+ setComputeLimitExceeded(computeUsage >= computeQuota);
+ },
+ onError: (e) => {
+ showErrorAnnouncer(t("common:usageSummaryError"), e);
+ },
+ });
+
+ const isLoading = isQueryLoading([status, isFetchingUsageSummary]);
+
+ if (isLoading) {
+ return ;
+ } else if (error) {
+ return (
+ <>
+ setErrorDialogOpen(true)}
+ />
+ setErrorDialogOpen(false)}
+ />
+ >
+ );
+ } else {
+ return (
+
+ );
+ }
+};
+
+export default withErrorAnnouncer(InstantLaunchStandalone);
diff --git a/src/pages/instantlaunch/[id]/index.js b/src/pages/instantlaunch/[id]/index.js
new file mode 100644
index 000000000..a3289106c
--- /dev/null
+++ b/src/pages/instantlaunch/[id]/index.js
@@ -0,0 +1,33 @@
+/**
+ * @author mian
+ *
+ * Instant launch launch page
+ */
+import React from "react";
+
+import { serverSideTranslations } from "next-i18next/serverSideTranslations";
+
+import { i18n, RequiredNamespaces } from "i18n";
+
+import { useRouter } from "next/router";
+
+import InstantLaunchStandalone from "components/instantlaunches/launch";
+
+export default function InstantLaunch() {
+ const router = useRouter();
+ const { id } = router.query;
+
+ return ;
+}
+
+export async function getServerSideProps({ locale }) {
+ const title = i18n.t("instantLaunch");
+
+ return {
+ props: {
+ title,
+ // "instantlaunches" already included by RequiredNamespaces
+ ...(await serverSideTranslations(locale, RequiredNamespaces)),
+ },
+ };
+}
diff --git a/src/server/api/instantlaunches.js b/src/server/api/instantlaunches.js
index 73c0e3544..8a885930c 100644
--- a/src/server/api/instantlaunches.js
+++ b/src/server/api/instantlaunches.js
@@ -165,6 +165,19 @@ export default () => {
})
);
+ logger.info("adding the GET /instantlaunches/:id/full handler");
+ api.get(
+ "/instantlaunches/:id/full",
+ auth.authnTokenMiddleware,
+ terrainHandler({
+ method: "GET",
+ pathname: "/instantlaunches/:id/full",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+ );
+
logger.info("Add the PUT /admin/instantlaunches/:id/metadata handler");
api.put(
"/admin/instantlaunches/:id/metadata",
diff --git a/src/serviceFacades/instantlaunches.js b/src/serviceFacades/instantlaunches.js
index 845e40b0f..6baf82577 100644
--- a/src/serviceFacades/instantlaunches.js
+++ b/src/serviceFacades/instantlaunches.js
@@ -20,6 +20,7 @@ export const DASHBOARD_INSTANT_LAUNCHES_KEY = "dashboardInstantLaunches";
export const LIST_PUBLIC_SAVED_LAUNCHES_KEY = "listPublicSavedLaunches";
export const LIST_INSTANT_LAUNCHES_BY_METADATA_KEY =
"fetchInstantLaunchesByMetadata";
+export const GET_INSTANT_LAUNCH_FULL_KEY = "fetchFullInstantLaunch";
export const getDefaultsMapping = () =>
callApi({
@@ -77,6 +78,12 @@ export const getInstantLaunchMetadata = (id) =>
method: "GET",
});
+export const getFullInstantLaunch = (id) =>
+ callApi({
+ endpoint: `/api/instantlaunches/${id}/full`,
+ method: "GET",
+ });
+
/**
* Add or modify the AVUs associated with an instant launch.
*