Skip to content

Commit

Permalink
Merge pull request #731 from airtai/dev
Browse files Browse the repository at this point in the history
Dev #729, #723
  • Loading branch information
kumaranvpl committed Sep 20, 2024
2 parents 5341156 + 99ea194 commit 5458fbd
Show file tree
Hide file tree
Showing 16 changed files with 938 additions and 102 deletions.
16 changes: 14 additions & 2 deletions app/src/client/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useMemo, useEffect, ReactNode, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';

import './Main.css';

Expand All @@ -12,6 +13,17 @@ import ServerNotRechableComponent from './components/ServerNotRechableComponent'
import LoadingComponent from './components/LoadingComponent';
import TosAndMarketingEmailsModal from './components/TosAndMarketingEmailsModal';

const theme = extendTheme({
colors: {
brand: {
airtPrimary: '#003257',
},
},
zIndices: {
tooltip: 10000,
},
});

const addServerErrorClass = () => {
if (!document.body.classList.contains('server-error')) {
document.body.classList.add('server-error');
Expand Down Expand Up @@ -114,7 +126,7 @@ export default function App({ children }: { children: ReactNode }) {
}, [location]);

return (
<>
<ChakraProvider theme={theme}>
<div className='bg-gradient-to-b from-airt-hero-gradient-start via-airt-hero-gradient-middle to-airt-secondary min-h-screen dark:text-white dark:bg-boxdark-2'>
{isError && (addServerErrorClass(), (<ServerNotRechableComponent />))}
{isAdminDashboard || isPlayGroundPage || isBuildPage ? (
Expand Down Expand Up @@ -153,7 +165,7 @@ export default function App({ children }: { children: ReactNode }) {
</div>
)}
</div>
</>
</ChakraProvider>
);
}

Expand Down
119 changes: 67 additions & 52 deletions app/src/client/components/buildPage/DynamicForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
popFromStackWithFormState,
} = usePropertyManager(parser, updateFormStack, refetchUserProperties, popFromStack);

const propertyName = parser?.getPropertyName() || '';
const isDeploymentProperty = propertyName === 'deployment';

const flow = parser?.getFlow();
const isAddModelFlow = flow === Flow.ADD_MODEL;
const isUpdateModelFlow = flow === Flow.UPDATE_MODEL;

const shouldShowDeploymentPrerequisites = isDeploymentProperty && isAddModelFlow && !successResponse;
const isDeploymentCreatedSuccessfully = isDeploymentProperty && isAddModelFlow && successResponse;
const shouldShowDeploymentInstructions = isDeploymentProperty && isUpdateModelFlow;
const checkForImmutableFields = isDeploymentCreatedSuccessfully || isUpdateModelFlow;

const addPropertyHandler = (modelName: string, propertyName: string, fieldKey: string) => {
popFromStackWithFormState(modelName, propertyName, fieldKey);
};
Expand All @@ -87,17 +99,26 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
}
const isReferenceField = parser?.checkIfRefField(property);
let propertyCopy = Object.assign({}, property);

let isOptionalRefField = false;
if (isReferenceField) {
const refFields = parser?.getRefFields();
if (refFields && refFields[key]) {
propertyCopy = refFields[key].htmlForSelectBox;
isOptionalRefField = refFields[key].isOptional;
const metadata = refFields[key]?.metadata;
if (metadata) {
propertyCopy.metadata = metadata;
}
}
} else {
const isNonRefButDropDownFields = parser?.getNonRefButDropdownFields();
if (isNonRefButDropDownFields && isNonRefButDropDownFields[key]) {
propertyCopy = isNonRefButDropDownFields[key].htmlForSelectBox;
const metadata = isNonRefButDropDownFields[key]?.metadata;
if (metadata) {
propertyCopy.metadata = metadata;
}
}
}
return (
Expand All @@ -111,6 +132,7 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
fieldKey={key}
isOptionalRefField={isOptionalRefField}
addPropertyHandler={addPropertyHandler}
checkForImmutableFields={checkForImmutableFields}
/>
)}
validators={{
Expand All @@ -122,7 +144,7 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
} else {
return null;
}
}, [schema, form]);
}, [schema, form, checkForImmutableFields]);

const cancelButtonRef = useRef<HTMLButtonElement>(null);
useEscapeKeyHandler(cancelButtonRef);
Expand All @@ -131,12 +153,6 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
setNotification(null);
};

const propertyName = parser?.getPropertyName() || '';
const isDeploymentProperty = propertyName === 'deployment';

const flow = parser?.getFlow();
const isAddUserFlow = flow === Flow.ADD_MODEL;

return (
<form
onSubmit={(e) => {
Expand All @@ -148,56 +164,55 @@ export const DynamicForm: React.FC<DynamicFormProps> = ({
{isLoading && <LoaderContainer />}
{notification && <NotificationBox type='error' onClick={notificationOnClick} message={notification} />}

{isDeploymentProperty && isAddUserFlow && !successResponse && (
<DeploymentPrerequisites successResponse={successResponse} />
)}
{shouldShowDeploymentPrerequisites && <DeploymentPrerequisites successResponse={successResponse} />}
{formFields}
{isDeploymentProperty && isAddUserFlow && successResponse && (
<DeploymentPrerequisites successResponse={successResponse} />
)}
{isDeploymentProperty && !isAddUserFlow && <DeploymentInstructions parser={parser} />}
{isDeploymentCreatedSuccessfully && <DeploymentPrerequisites successResponse={successResponse} />}
{shouldShowDeploymentInstructions && <DeploymentInstructions parser={parser} />}

<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<>
{isSubmitting && <LoaderContainer />}
<div className='col-span-full mt-7'>
<div className='float-right'>
<button
className='rounded-md px-3.5 py-2.5 text-sm border border-airt-error text-airt-primary hover:bg-opacity-10 hover:bg-airt-error shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
type='reset'
onClick={() => handleCtaAction('cancel')}
ref={cancelButtonRef}
disabled={isSubmitting}
>
Cancel
</button>
<button
data-testid='form-submit-button'
type='submit'
className={`ml-3 rounded-md px-3.5 py-2.5 text-sm bg-airt-primary text-airt-font-base hover:bg-opacity-85 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 ${
!canSubmit ? 'cursor-not-allowed disabled' : ''
}`}
disabled={!canSubmit}
>
{isSubmitting ? 'Saving...' : 'Save'}
</button>
children={([canSubmit, isSubmitting]) => {
const disableSaveButton = !canSubmit || isDeploymentCreatedSuccessfully;
return (
<>
{isSubmitting && <LoaderContainer />}
<div className='col-span-full mt-7'>
<div className='float-right'>
<button
className='rounded-md px-3.5 py-2.5 text-sm border border-airt-error text-airt-primary hover:bg-opacity-10 hover:bg-airt-error shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
type='reset'
onClick={() => handleCtaAction('cancel')}
ref={cancelButtonRef}
disabled={isSubmitting}
>
Cancel
</button>
<button
data-testid='form-submit-button'
type='submit'
className={`ml-3 rounded-md px-3.5 py-2.5 text-sm bg-airt-primary text-airt-font-base hover:bg-opacity-85 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 ${
disableSaveButton ? 'cursor-not-allowed disabled' : ''
}`}
disabled={disableSaveButton}
>
{isSubmitting ? 'Saving...' : 'Save'}
</button>
</div>
{parser?.getFlow() === 'update_model' && (
<button
type='button'
className='float-left rounded-md px-3.5 py-2.5 text-sm border bg-airt-error text-airt-font-base hover:bg-opacity-80 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
disabled={isSubmitting}
data-testid='form-cancel-button'
onClick={() => handleCtaAction('delete')}
>
Delete
</button>
)}
</div>
{parser?.getFlow() === 'update_model' && (
<button
type='button'
className='float-left rounded-md px-3.5 py-2.5 text-sm border bg-airt-error text-airt-font-base hover:bg-opacity-80 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
disabled={isSubmitting}
data-testid='form-cancel-button'
onClick={() => handleCtaAction('delete')}
>
Delete
</button>
)}
</div>
</>
)}
</>
);
}}
/>
</form>
);
Expand Down
27 changes: 26 additions & 1 deletion app/src/client/components/buildPage/FormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
import _ from 'lodash';
import { FieldApi } from '@tanstack/react-form';
import Select, { StylesConfig } from 'react-select';
import { Tooltip } from '@chakra-ui/react';
import { IoIosInformationCircleOutline } from 'react-icons/io';

import { TextInput } from '../form/TextInput';
import { SECRETS_TO_MASK } from '../../utils/constants';
Expand Down Expand Up @@ -37,6 +39,7 @@ interface FormFieldProps {
fieldKey: string;
isOptionalRefField: boolean;
addPropertyHandler: AddPropertyHandler;
checkForImmutableFields: boolean;
}

function FieldInfo({ field }: { field: FieldApi<any, any, any, any> }) {
Expand All @@ -62,6 +65,7 @@ export const FormField: React.FC<FormFieldProps> = ({
fieldKey,
isOptionalRefField,
addPropertyHandler,
checkForImmutableFields,
}) => {
const [selectOptions, setSelectOptions] = useState([]);
const [defaultValue, setDefaultValue] = useState(null);
Expand Down Expand Up @@ -103,9 +107,27 @@ export const FormField: React.FC<FormFieldProps> = ({
}
};

const immutableAfterCreation = checkForImmutableFields && property.metadata?.immutable_after_creation;
const toolTipMessage = property.metadata?.tooltip_message;

return (
<div className='w-full mt-2'>
<label htmlFor={fieldKey}>{`${property.title} ${isOptionalRefField ? ' (Optional)' : ''}`}</label>
<label htmlFor={fieldKey} className='flex items-center'>
{`${property.title} ${isOptionalRefField ? ' (Optional)' : ''}`}
{toolTipMessage && (
<Tooltip
hasArrow
label={toolTipMessage}
className='bg-airt-secondary text-airt-font-base'
bg='brand.airtPrimary'
color='white'
>
<span className='ml-1 text-airt-primary' data-testid={`${fieldKey}-tooltip`}>
<IoIosInformationCircleOutline />
</span>
</Tooltip>
)}
</label>
{property.enum ? (
<Select
key={key}
Expand All @@ -119,13 +141,15 @@ export const FormField: React.FC<FormFieldProps> = ({
isSearchable={true}
isClearable={isOptionalRefField}
styles={markAddPropertyOption}
isDisabled={immutableAfterCreation}
/>
) : fieldKey === 'system_message' ? (
<TextArea
id={fieldKey}
value={field.state.value}
placeholder={property.description || ''}
onChange={(e) => field.handleChange(e)}
disabled={immutableAfterCreation}
/>
) : (
<TextInput
Expand All @@ -134,6 +158,7 @@ export const FormField: React.FC<FormFieldProps> = ({
onChange={(e) => field.handleChange(e)}
type={getInputType()}
placeholder={property.description || ''}
disabled={immutableAfterCreation}
/>
)}
<FieldInfo field={field} />
Expand Down
12 changes: 12 additions & 0 deletions app/src/client/components/buildPage/PropertySchemaParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export class PropertySchemaParser implements PropertySchemaParserInterface {
.join(' ');
}

private addMetadataIfExists(field: any, property: any): void {
if ('metadata' in property) {
field.metadata = property.metadata;
}
}

public getSchemaForModel(): Schema | undefined {
if (!this.activeModel) {
return undefined;
Expand Down Expand Up @@ -164,6 +170,9 @@ export class PropertySchemaParser implements PropertySchemaParserInterface {
initialFormValue: defaultValue?.value ?? null,
isOptional: isOptional,
};

this.addMetadataIfExists(this.refFields[key], property);

defaultValues[key] = this.refFields[key].initialFormValue;
}

Expand Down Expand Up @@ -213,6 +222,9 @@ export class PropertySchemaParser implements PropertySchemaParserInterface {
},
initialFormValue: defaultValue?.value ?? null,
};

this.addMetadataIfExists(this.nonRefButDropdownFields[key], property);

defaultValues[key] = this.nonRefButDropdownFields[key].initialFormValue;
}

Expand Down
5 changes: 3 additions & 2 deletions app/src/client/components/buildPage/usePropertyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@ export const usePropertyManager = (
const response = await onSuccessCallback(validatedData, propertyName, modelName, propertyUUIDToUpdate);
await setSuccessResponse(response);

if (!isDeploymentProperty) {
await resetAndRefetchProperties(validatedData);
if (isDeploymentProperty && flow === Flow.ADD_MODEL) {
return;
}
await resetAndRefetchProperties(validatedData);
} catch (error: any) {
try {
const errorMsgObj = JSON.parse(error.message);
Expand Down
6 changes: 4 additions & 2 deletions app/src/client/components/form/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ interface TextAreaProps {
value: string;
placeholder: string;
onChange: (value: string) => void;
disabled?: boolean;
}

export const TextArea: React.FC<TextAreaProps> = ({ id, value, placeholder, onChange }) => (
export const TextArea: React.FC<TextAreaProps> = ({ id, value, placeholder, onChange, disabled }) => (
<textarea
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
className='my-2 p-2 border rounded w-full h-40'
className='my-2 p-2 border rounded w-full h-40 disabled:bg-airt-disabled-input-bg disabled:text-airt-disabled-input-color'
id={id}
disabled={disabled}
/>
);
6 changes: 4 additions & 2 deletions app/src/client/components/form/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ interface TextInputProps {
value: string;
placeholder: string;
onChange: (value: string) => void;
disabled?: boolean;
}

export const TextInput: React.FC<TextInputProps> = ({ id, type, value, placeholder, onChange }) => (
export const TextInput: React.FC<TextInputProps> = ({ id, type, value, placeholder, onChange, disabled }) => (
<input
type={type}
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
className='my-2 p-2 border rounded w-full'
className='my-2 p-2 border rounded w-full disabled:bg-airt-disabled-input-bg disabled:text-airt-disabled-input-color'
id={id}
disabled={disabled}
/>
);
1 change: 1 addition & 0 deletions app/src/client/interfaces/BuildPageInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface Property {
minimum?: number;
anyOf?: SchemaReference[];
$ref?: string;
metadata?: { [key: string]: any };
}

interface SchemaReference {
Expand Down
Loading

0 comments on commit 5458fbd

Please sign in to comment.