Skip to content

Commit

Permalink
rebase
Browse files Browse the repository at this point in the history
  • Loading branch information
Sara4994 committed Nov 9, 2023
1 parent 152b672 commit 823107b
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 1 deletion.
4 changes: 3 additions & 1 deletion frontend/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { Route, Routes } from 'react-router-dom';
import ApplicationsPage from '~/pages/ApplicationsPage';
import UnauthorizedError from '~/pages/UnauthorizedError';
import EdgeModelRegistry from '';
import { useUser } from '~/redux/selectors';

Check failure on line 6 in frontend/src/app/AppRoutes.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

`~/redux/selectors` import should occur before import of ``

const InstalledApplications = React.lazy(
Expand Down Expand Up @@ -31,6 +32,7 @@ const CustomServingRuntimeRoutes = React.lazy(
const GroupSettingsPage = React.lazy(() => import('../pages/groupSettings/GroupSettings'));
const LearningCenterPage = React.lazy(() => import('../pages/learningCenter/LearningCenter'));
const BYONImagesPage = React.lazy(() => import('../pages/BYONImages/BYONImages'));
const EdgeModelRegistry = React.lazy(() => import('../pages/edgeMVP/EdgeModelRegistry'));
const NotFound = React.lazy(() => import('../pages/NotFound'));

const DependencyMissingPage = React.lazy(
Expand Down Expand Up @@ -80,7 +82,7 @@ const AppRoutes: React.FC = () => {
<Route path="/groupSettings" element={<GroupSettingsPage />} />
</>
)}

<Route path="/modelRegistry" element={<EdgeModelRegistry />} />
<Route path="*" element={<NotFound />} />
</Routes>
</React.Suspense>
Expand Down
265 changes: 265 additions & 0 deletions frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import { Button, Checkbox, Form, FormGroup, FormSelect, FormSelectOption, Modal, ModalVariant, Radio, Stack, StackItem, TextInput } from '@patternfly/react-core';

Check failure on line 1 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Replace `·Button,·Checkbox,·Form,·FormGroup,·FormSelect,·FormSelectOption,·Modal,·ModalVariant,·Radio,·Stack,·StackItem,·TextInput·` with `⏎··Button,⏎··Checkbox,⏎··Form,⏎··FormGroup,⏎··FormSelect,⏎··FormSelectOption,⏎··Modal,⏎··ModalVariant,⏎··Radio,⏎··Stack,⏎··StackItem,⏎··TextInput,⏎`

Check failure on line 1 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

'Checkbox' is defined but never used
import * as React from 'react';

type AddModelToRegistryModalProps = {
isOpen: boolean;

Check failure on line 5 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Replace `····` with `··`
onClose: (submit: boolean) => void;

Check failure on line 6 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Delete `··`
}

Check failure on line 7 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Insert `;`

const AddModelToRegistryModal: React.FC<AddModelToRegistryModalProps> = ({ isOpen, onClose }) => {
const [isDefault, setIsDefault] = React.useState<boolean>(true);

Check failure on line 10 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Replace `····` with `··`
const [modelName, setModelName] = React.useState<string>('');

Check failure on line 11 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Delete `··`
const [modelFramework, setModelFramework] = React.useState<string>('PyTorch');

Check failure on line 12 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Replace `····` with `··`
const [s3BucketData, setS3BucketData] = React.useState({

Check failure on line 13 in frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx

View workflow job for this annotation

GitHub Actions / Tests (18.x)

Delete `··`
accessKey: '',
secretKey: '',
endpoint: '',
region: '',
bucket: '',
path: ''
});
const [gitRepoData, setGitRepoData] = React.useState({
apiToken: '',
url: '',
branch: '',
modelDirectory: ''
});

const options = [
{ value: 'PyTorch', label: 'PyTorch', disabled: false, isPlaceholder: false },
{ value: 'TensorFlow', label: 'TensorFlow', disabled: false, isPlaceholder: false },
{ value: 'OpenVino', label: 'OpenVino', disabled: false, isPlaceholder: false },
];

const onChange = (value: string, _event: React.FormEvent<HTMLSelectElement>) => {
setModelFramework(value);
};

return (
<Modal
bodyAriaLabel="Scrollable modal content"
tabIndex={0}
variant={ModalVariant.small}
title="Add model"
isOpen={isOpen}
onClose={() => onClose(false)}
actions={[
<Button key="confirm" variant="primary" onClick={() => { }}>
Add model
</Button>,
<Button key="cancel" variant="link" onClick={() => { }}>
Cancel
</Button>
]}
>
<Form>
<Stack hasGutter>
<StackItem>
<FormGroup label="Model Name" fieldId="add-model-name-input" isRequired>
<TextInput
isRequired
id="inference-service-name-input"
value={modelName}
onChange={(name) => setModelName(name)}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Model framework" fieldId="add-model-framework-input" isRequired>
<FormSelect
id="model-framework-selection"
value={modelFramework}
onChange={onChange}
aria-label="Model framework selection"
>
{options.map((option, index) => (
<FormSelectOption
isDisabled={option.disabled}
key={index}
value={option.value}
label={option.label}
isPlaceholder={option.isPlaceholder}
/>
))}
</FormSelect>
</FormGroup>
</StackItem>
<FormGroup role="group" isInline fieldId="model-location" label="Model location">
<StackItem>
<Radio
label="S3 bucket"
isChecked={isDefault}
onChange={() => {
setIsDefault(true)
}}
id="model-registry-s3-bucket-radio"
name="s3-bucket-radio"
body={
<Stack hasGutter>
<StackItem>
<FormGroup label="Access key" fieldId="s3-access-key-input" isRequired>
<TextInput
isRequired
id="s3-access-key-input"
name="s3-access-key-input"
aria-describedby="s3-access-key-input"
value={s3BucketData.accessKey}
onChange={(accessKey) => setS3BucketData({ ...s3BucketData, accessKey })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Secret key" fieldId="s3-secret-key-input" isRequired>
<TextInput
isRequired
id="s3-secret-key-input"
name="s3-secret-key-input"
aria-describedby="s3-secret-key-input"
value={s3BucketData.secretKey}
onChange={(secretKey) => setS3BucketData({ ...s3BucketData, secretKey })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Endpoint" fieldId="s3-endpoint-name-input" isRequired>
<TextInput
isRequired
id="s3-endpoint-name-input"
name="s3-endpoint-name-input"
aria-describedby="s3-endpoint-name-input"
value={s3BucketData.endpoint}
onChange={(endpoint) => setS3BucketData({ ...s3BucketData, endpoint })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Region" fieldId="s3-region-input">
<TextInput
isRequired
id="s3-region-input"
name="s3-region-input"
aria-describedby="s3-region-input"
value={s3BucketData.region}
onChange={(region) => setS3BucketData({ ...s3BucketData, region })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Bucket" fieldId="s3-bucket-name-input">
<TextInput
isRequired
id="s3-bucket-name-input"
name="s3-bucket-name-input"
aria-describedby="s3-bucket-name-input"
value={s3BucketData.bucket}
onChange={(bucket) => setS3BucketData({ ...s3BucketData, bucket })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Path" fieldId="s3-bucket-path-input" isRequired>
<TextInput
isRequired
id="s3-bucket-path-input"
name="s3-bucket-path-input"
aria-describedby="s3-bucket-path-input"
value={s3BucketData.path}
onChange={(path) => setS3BucketData({ ...s3BucketData, path })}
isDisabled={!isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
</Stack>
}
/>
</StackItem>
<StackItem>
<Radio
label="Git repository"
isChecked={!isDefault}
onChange={() => {
setIsDefault(false)
}}
id="model-registry-git-respository-radio"
name="git-repository-radio"
body={
<Stack hasGutter>
<StackItem>
<FormGroup label="API Token" fieldId="github-api-token-input" isRequired>
<TextInput
isRequired
id="github-api-token-input"
name="github-api-token-input"
aria-describedby="github-api-token-input"
value={gitRepoData.apiToken}
onChange={(apiToken) => setGitRepoData({ ...gitRepoData, apiToken })}
isDisabled={isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="URL" fieldId="github-url-input" isRequired>
<TextInput
isRequired
id="github-url-input"
name="github-url-input"
aria-describedby="github-url-input"
value={gitRepoData.url}
onChange={(url) => setGitRepoData({ ...gitRepoData, url })}
isDisabled={isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Branch" fieldId="github-branch-name-input" isRequired>
<TextInput
isRequired
id="github-branch-name-input"
name="github-branch-name-input"
aria-describedby="github-branch-name-input"
value={gitRepoData.branch}
onChange={(branch) => setGitRepoData({ ...gitRepoData, branch })}
isDisabled={isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
<StackItem>
<FormGroup label="Model directory" fieldId="github-model-directory-input" isRequired>
<TextInput
isRequired
id="github-model-directory-input"
name="github-model-directory-input"
aria-describedby="github-model-directory-input"
value={gitRepoData.modelDirectory}
onChange={(modelDirectory) => setGitRepoData({ ...gitRepoData, modelDirectory })}
isDisabled={isDefault}
style={{ width: '400px' }}
/>
</FormGroup>
</StackItem>
</Stack>
}
/>
</StackItem>
</FormGroup>
</Stack>
</Form>
</Modal>
)
}

export default AddModelToRegistryModal;
50 changes: 50 additions & 0 deletions frontend/src/pages/edgeMVP/EdgeModelRegistry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import _ from 'lodash';
import ApplicationsPage from '~/pages/ApplicationsPage';
import { OdhApplication } from '~/types';
import { useWatchComponents } from '~/utilities/useWatchComponents';
import {
Bullseye,
} from '@patternfly/react-core';
import EmptyModelRegistry from './EmptyModelRegistry';

type EdgeModelRegistryInnerProps = {
loaded: boolean;
loadError?: Error;
components: OdhApplication[];
};

const EdgeModelRegistryInner: React.FC<EdgeModelRegistryInnerProps> = React.memo(({ loaded, loadError, components }) => {

return (
<ApplicationsPage
title="Model registry"
description={''}
loaded={true}
empty={false}
>
<Bullseye>
<EmptyModelRegistry />
</Bullseye>
</ApplicationsPage>
)
})

EdgeModelRegistryInner.displayName = 'EdgeModelRegistryInner';

const EdgeModelRegistry: React.FC = () => {
const { components, loaded, loadError } = useWatchComponents(true);

const sortedComponents = React.useMemo(
() =>
_.cloneDeep(components).sort((a, b) => a.spec.displayName.localeCompare(b.spec.displayName)),
[components],
);


return (
<EdgeModelRegistryInner loaded={loaded} components={sortedComponents} loadError={loadError} />
);
};

export default EdgeModelRegistry;
25 changes: 25 additions & 0 deletions frontend/src/pages/edgeMVP/EmptyModelRegistry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import {
EmptyState,
EmptyStateBody,
EmptyStateIcon,
Title
} from '@patternfly/react-core';
import { PlusCircleIcon } from '@patternfly/react-icons';
import ImportModelButton from './ImportModelButton';

const EmptyModelRegistry: React.FC = () => {

return (
<EmptyState>
<EmptyStateIcon icon={PlusCircleIcon} />
<Title headingLevel="h2" size="lg" style={{ marginBottom: '10px' }}>
No models in the registry
</Title>
<EmptyStateBody style={{ margin: 'auto', width: '50%' }}>To get started, add a model to the model registry. Adding a model will also initiate a pipeline that will build the model and its dependencies into a container image and save that image in a container image registry.</EmptyStateBody>
<ImportModelButton />
</EmptyState>
)
}

export default EmptyModelRegistry;
21 changes: 21 additions & 0 deletions frontend/src/pages/edgeMVP/ImportModelButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Button } from '@patternfly/react-core';
import * as React from 'react';
import AddModelToRegistryModal from './AddModelToRegistryModal';

const ImportModelButton: React.FC = () => {
const [open, setOpen] = React.useState(false);
return (
<>
<Button variant="primary" onClick={() => setOpen(true)}>
Add model
</Button>
<AddModelToRegistryModal isOpen={open}
onClose={(submit) => {
setOpen(false);
}}
/>
</>
)
}

export default ImportModelButton;
Loading

0 comments on commit 823107b

Please sign in to comment.