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

WP-710: For apps with no inputs, only show launch button #1469

Merged
merged 9 commits into from
Oct 11, 2024
2 changes: 2 additions & 0 deletions client/modules/_hooks/src/workspace/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ export type TTapisApp = {
queueFilter?: string[];
hideQueue?: boolean;
hideAllocation?: boolean;
hideMaxMinutes?: boolean;
jobLaunchDescription?: string;
};
uuid: string;
deleted: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { useFormContext, useWatch, FieldValues } from 'react-hook-form';
import { z, ZodTypeAny } from 'zod';
import { TField, fieldDisplayOrder } from '../AppsWizard/AppsFormSchema';
import { PrimaryButton } from '@client/common-components';
import { JobSubmitButton } from '../JobSubmitButton/JobSubmitButton';
import styles from './AppsSubmissionDetails.module.css';
import { TTapisApp } from '@client/hooks';

Expand Down Expand Up @@ -255,14 +255,11 @@ export const AppsSubmissionDetails: React.FC<{
style={itemStyle}
className={styles.root}
extra={
<PrimaryButton
htmlType="submit"
<JobSubmitButton
disabled={!isValid}
loading={isSubmitting}
style={{ width: 150 }}
>
{definition.notes.isInteractive ? 'Launch Session' : 'Submit Job'}
</PrimaryButton>
interactive={definition.notes.isInteractive}
/>
}
/>
</Card>
Expand Down
125 changes: 79 additions & 46 deletions client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { NavLink } from 'react-router-dom';
import { Layout, Form, Col, Row, Alert, Button } from 'antd';
import { Layout, Form, Col, Row, Alert, Button, Space } from 'antd';
import { z } from 'zod';
import { useForm, FormProvider } from 'react-hook-form';
import { Link } from 'react-router-dom';
import { zodResolver } from '@hookform/resolvers/zod';
import { JobSubmitButton } from '../JobSubmitButton/JobSubmitButton';
import {
useGetAppsSuspense,
useGetJobSuspense,
Expand Down Expand Up @@ -134,7 +135,7 @@
),
outputs: outputs.defaults,
}),
[definition, jobData]

Check warning on line 138 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useMemo has missing dependencies: 'appId', 'appVersion', 'configuration.defaults', 'fileInputs.defaults', 'outputs.defaults', and 'parameterSet.defaults'. Either include them or remove the dependency array
);

let missingAllocation: string | undefined;
Expand Down Expand Up @@ -179,12 +180,14 @@
type FieldNameUnion = keyof typeof fieldValues;

const getSteps = (): TStep => {
const formSteps: TStep = {
configuration: getConfigurationStep(configuration.fields),
...(definition.notes.isInteractive
? {}
: { outputs: getOutputsStep(outputs.fields) }),
};
const formSteps: TStep = {};

if (configuration.fields && Object.keys(configuration.fields).length) {
formSteps.configuration = getConfigurationStep(configuration.fields);
}
if (!definition.notes.isInteractive) {
formSteps.outputs = getOutputsStep(outputs.fields);
}
if (fileInputs.fields && Object.keys(fileInputs.fields).length) {
formSteps.inputs = getInputsStep(fileInputs.fields);
}
Expand Down Expand Up @@ -222,16 +225,16 @@
outputs: outputs.fields,
});

const initialSteps = useMemo(
() => getSteps(),
[
fileInputs.fields,
parameterSet.fields,
configuration.fields,
outputs.fields,
fields,
]
);
const initialSteps = useMemo(() => {
const steps = getSteps();
return Object.keys(steps).length > 0 ? steps : {};
}, [

Check warning on line 231 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useMemo has a missing dependency: 'getSteps'. Either include it or remove the dependency array
fileInputs.fields,
parameterSet.fields,
configuration.fields,
outputs.fields,
fields,
]);

const getInitialCurrentStep = (steps: TStep) => {
if (steps.inputs) return 'inputs';
Expand All @@ -246,7 +249,7 @@
const newSteps = getSteps();
setSteps(newSteps);
setCurrent(getInitialCurrentStep(newSteps));
}, [initialValues]);

Check warning on line 252 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useEffect has missing dependencies: 'getInitialCurrentStep', 'getSteps', and 'reset'. Either include them or remove the dependency array

// Queue dependency handler.
const queueValue = watch('configuration.execSystemLogicalQueue');
Expand Down Expand Up @@ -294,7 +297,7 @@
configuration: updatedFields,
}));
}
}, [queueValue, setValue]);

Check warning on line 300 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook React.useEffect has missing dependencies: 'allocations', 'definition', 'execSystems', and 'getValues'. Either include them or remove the dependency array

// TODO: DES-2916: Use Zod's superRefine feature instead of manually updating schema and tracking schema changes.
React.useEffect(() => {
Expand All @@ -316,24 +319,26 @@
}

// Note: currently configuration is the only
// step that needs. This can be more generic
// step that needs update. This can be more generic
// in future if the fields shape is same between
// Step and Submission Detail View (mostly related to env vars)
useEffect(() => {
const updatedConfigurationStep = getConfigurationStep(
fields.configuration as { [key: string]: TField }
);
if (configuration.fields && Object.keys(configuration.fields).length) {
const updatedConfigurationStep = getConfigurationStep(
fields.configuration as { [key: string]: TField }
);

const updatedSteps: TStep = {
...steps,
configuration: {
...steps.configuration,
...updatedConfigurationStep,
},
};
const updatedSteps: TStep = {
...steps,
configuration: {
...steps.configuration,
...updatedConfigurationStep,
},
};

setSteps(updatedSteps);
setSteps(updatedSteps);
}
}, [fields]);

Check warning on line 341 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useEffect has missing dependencies: 'configuration.fields' and 'steps'. Either include them or remove the dependency array

// next step transition does not block on invalid fields
const handleNextStep = useCallback(async () => {
Expand All @@ -343,11 +348,11 @@
await methods.trigger(stepFields);
const nextPage = steps[current].nextPage;
nextPage && setCurrent(nextPage);
}, [current, methods]);

Check warning on line 351 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useCallback has missing dependencies: 'fieldValues' and 'steps'. Either include them or remove the dependency array
const handlePreviousStep = useCallback(() => {
const prevPage = steps[current].prevPage;
prevPage && setCurrent(prevPage);
}, [current]);

Check warning on line 355 in client/modules/workspace/src/AppsSubmissionForm/AppsSubmissionForm.tsx

View workflow job for this annotation

GitHub Actions / React_NX_linting

React Hook useCallback has a missing dependency: 'steps'. Either include it or remove the dependency array
const {
mutate: submitJob,
isPending,
Expand Down Expand Up @@ -615,23 +620,51 @@
>
<fieldset disabled={readOnly}>
<Row gutter={[64, 16]} align="top">
<Col span={14}>
<AppsWizard
step={steps[current]}
handlePreviousStep={handlePreviousStep}
handleNextStep={handleNextStep}
/>
</Col>
<Col span={10}>
<AppsSubmissionDetails
schema={schema}
fields={fields}
isSubmitting={isPending}
current={current}
setCurrent={setCurrent}
definition={definition}
/>
</Col>
{Object.keys(steps || {}).length === 0 ? (
<Col style={{ marginTop: '32px', marginLeft: '32px' }}>
<Space direction="vertical" size="large">
<div>
{isSuccess ? (
<span>
Session has been launched. You can view status in{' '}
<strong>Job Status</strong>.
</span>
) : (
definition.notes.jobLaunchDescription ??
'This job is pre-configured. No input is necessary to submit the job.'
)}
</div>
<div>
<JobSubmitButton
loading={isPending}
interactive={definition.notes.isInteractive}
disabled={isPending || isSuccess}
success={isSuccess}
/>
</div>
</Space>
</Col>
) : (
<>
<Col span={14}>
<AppsWizard
step={steps[current]}
handlePreviousStep={handlePreviousStep}
handleNextStep={handleNextStep}
/>
</Col>
<Col span={10}>
<AppsSubmissionDetails
schema={schema}
fields={fields}
isSubmitting={isPending}
current={current}
setCurrent={setCurrent}
definition={definition}
/>
</Col>
</>
)}
</Row>
</fieldset>
</Form>
Expand Down
43 changes: 25 additions & 18 deletions client/modules/workspace/src/AppsWizard/AppsFormSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,13 @@ export const getConfigurationSchema = (
);
}

configurationSchema['maxMinutes'] = getMaxMinutesValidation(
definition,
queue
);
if (!definition.notes.hideMaxMinutes) {
configurationSchema['maxMinutes'] = getMaxMinutesValidation(
definition,
queue
);
}

if (!definition.notes.hideNodeCountAndCoresPerNode) {
configurationSchema['nodeCount'] = getNodeCountValidation(
definition,
Expand Down Expand Up @@ -216,18 +219,20 @@ export const getConfigurationFields = (
};
}

configurationFields['maxMinutes'] = {
description: `The maximum number of minutes you expect this job to run for. Maximum possible is ${getQueueMaxMinutes(
definition,
defaultExecSystem,
queue?.name
)} minutes. After this amount of time your job will end. Shorter run times result in shorter queue wait times.`,
label: 'Maximum Job Runtime (minutes)',
name: 'configuration.maxMinutes',
key: 'configuration.maxMinutes',
required: true,
type: 'number',
};
if (!definition.notes.hideMaxMinutes) {
configurationFields['maxMinutes'] = {
description: `The maximum number of minutes you expect this job to run for. Maximum possible is ${getQueueMaxMinutes(
definition,
defaultExecSystem,
queue?.name
)} minutes. After this amount of time your job will end. Shorter run times result in shorter queue wait times.`,
label: 'Maximum Job Runtime (minutes)',
name: 'configuration.maxMinutes',
key: 'configuration.maxMinutes',
required: true,
type: 'number',
};
}

if (!definition.notes.hideNodeCountAndCoresPerNode) {
configurationFields['nodeCount'] = {
Expand Down Expand Up @@ -509,8 +514,10 @@ const FormSchema = (
: '';
}

appFields.configuration.defaults['maxMinutes'] =
definition.jobAttributes.maxMinutes;
if (!definition.notes.hideMaxMinutes) {
appFields.configuration.defaults['maxMinutes'] =
definition.jobAttributes.maxMinutes;
}

if (!definition.notes.hideNodeCountAndCoresPerNode) {
appFields.configuration.defaults['nodeCount'] =
Expand Down
21 changes: 21 additions & 0 deletions client/modules/workspace/src/JobSubmitButton/JobSubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { PrimaryButton } from '@client/common-components';

export const JobSubmitButton: React.FC<{
loading: boolean;
interactive: boolean | undefined;
disabled?: boolean;
success?: boolean;
}> = ({ loading, interactive, disabled = false, success = false }) => {
return (
<PrimaryButton
disabled={disabled}
htmlType="submit"
loading={loading}
style={{ width: 150 }}
>
{interactive ? 'Launch Session' : 'Submit Job'}
{success && <i className="fa fa-check" style={{ marginLeft: 5 }} />}
</PrimaryButton>
);
};
Loading