Skip to content

Commit

Permalink
feat(cosnole): when create workload add register dialog - for master (t…
Browse files Browse the repository at this point in the history
…kestack#2143)

* feat(console): add change displayName

* feat(console): add register select dialog
  • Loading branch information
jo-hnny committed Nov 3, 2022
1 parent 92090b5 commit 4945e11
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { EditResourceContainerAdvancedPanel } from './EditResourceContainerAdvan
import { EditResourceContainerEnvItem } from './EditResourceContainerEnvItem';
import { EditResourceContainerLimitItem } from './EditResourceContainerLimitItem';
import { EditResourceContainerMountItem } from './EditResourceContainerMountItem';
import { RegistrySelectDialog, RegistryTagSelectDialog } from './registrySelect';

interface ContainerItemProps extends RootProps {
/** 容器的id */
Expand All @@ -43,6 +44,7 @@ const mapDispatchToProps = dispatch =>

@connect(state => state, mapDispatchToProps)
export class EditResourceContainerItem extends React.Component<ContainerItemProps, {}> {
state: Readonly<{ tags: any[] }> = { tags: null };
render() {
let { actions, subRoot, cKey, clusterVersion, cluster } = this.props,
{ workloadEdit, addons } = subRoot,
Expand Down Expand Up @@ -132,22 +134,39 @@ export class EditResourceContainerItem extends React.Component<ContainerItemProp
value={container.registry}
onChange={e => {
actions.editWorkload.updateContainer({ registry: e.target.value }, cKey);

this.setState({ tags: null });
}}
onBlur={e => actions.validate.workload.validateRegistrySelection(e.target.value, cKey)}
/>
</Bubble>

<RegistrySelectDialog
onConfirm={({ registry, tags }) => {
actions.editWorkload.updateContainer({ registry }, cKey);

this.setState({ tags });
}}
/>
</div>
</FormItem>
<FormItem label={t('镜像版本(Tag)')} className="tag-mod">
<div className="tc-15-autocomplete xl">
<input
type="text"
className="tc-15-input-text m"
className="tc-15-input-text m mr10"
value={container.tag}
onChange={e => {
actions.editWorkload.updateContainer({ tag: e.target.value }, cKey);
}}
/>

{this.state.tags && (
<RegistryTagSelectDialog
tags={this.state.tags}
onConfirm={tag => actions.editWorkload.updateContainer({ tag }, cKey)}
/>
)}
</div>
</FormItem>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { RegistrySelectDialog } from './registrySelectDialog';

export { RegistryTagSelectDialog } from './registryTagSelectDialog';
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { useMemo, useState } from 'react';
import { Button, Modal, Table, SearchBox, TableColumn, Text } from 'tea-component';
import { fetchRepositoryList } from '@src/webApi/registry';
import { useRequest } from 'ahooks';
import { fetchDockerRegUrl } from '@src/modules/registry/WebAPI';

const { filterable, scrollable, radioable } = Table.addons;

const ALL_VALUE = '__ALL__';

export const RegistrySelectDialog = ({ onConfirm }) => {
const [visible, setVisible] = useState(false);

const [selectedRepo, setSelectedRepo] = useState(null);

const [searchValue, setSearchValue] = useState('');

const [visibilityType, setVisibilityType] = useState(ALL_VALUE);

const [selectedNamespaceList, setSelectedNamespaceList] = useState([]);

const { data: repoBaseUrl } = useRequest(async () => {
const res = await fetchDockerRegUrl();

console.log('repoBaseUrl---->', res);

return res;
});

const { data: _repositoryList } = useRequest(async () => {
const res = await fetchRepositoryList();

return res?.items ?? [];
});

const repositoryList = useMemo(() => {
return (
_repositoryList
?.filter(repo => repo?.spec?.name?.includes(searchValue))
?.filter(repo => visibilityType === ALL_VALUE || repo?.spec?.visibility === visibilityType)
?.filter(
repo => selectedNamespaceList?.length === 0 || selectedNamespaceList?.includes(repo?.spec?.namespaceName)
) ?? []
);
}, [_repositoryList, searchValue, visibilityType, selectedNamespaceList]);

const namespaceOptions = useMemo(() => {
return _repositoryList?.map(repo => ({ value: repo?.spec?.namespaceName })) ?? [];
}, [_repositoryList]);

const columns: TableColumn[] = [
{
key: 'spec.name',
header: '名称'
},

{
key: 'spec.visibility',
header: '类型'
},

{
key: 'spec.namespaceName',
header: '命名空间'
},

{
key: 'spec.resourceVersion',
header: '仓库地址',
render(repo) {
return <Text overflow copyable>{`${repoBaseUrl}/${repo?.spec?.namespaceName}/${repo?.spec?.name}`}</Text>;
}
}
];

return (
<>
<Button type="link" onClick={() => setVisible(true)}>
选择镜像
</Button>

<Modal caption="选择镜像" visible={visible} onClose={() => setVisible(false)}>
<Modal.Body>
<Table.ActionPanel>
<SearchBox value={searchValue} onChange={value => setSearchValue(value)} />
</Table.ActionPanel>

<Table
columns={columns}
records={repositoryList}
recordKey="metadata.name"
addons={[
filterable({
type: 'single',
column: 'spec.visibility',
value: visibilityType,
onChange: value => setVisibilityType(value),
all: {
value: ALL_VALUE,
text: '全部'
},
// 选项列表
options: [
{ value: 'Public', text: '公有' },
{ value: 'Private', text: '私有' }
]
}),

filterable({
type: 'multiple',
column: 'spec.namespaceName',
value: selectedNamespaceList,
onChange: value => {
setSelectedNamespaceList(value);
},
all: {
value: ALL_VALUE,
text: '全部'
},
options: namespaceOptions
}),

scrollable({
maxHeight: 500
}),

radioable({
value: selectedRepo,
onChange(key) {
setSelectedRepo(key);
},
rowSelect: true
})
]}
/>
</Modal.Body>

<Modal.Footer>
<Button
disabled={!selectedRepo}
type="primary"
onClick={() => {
const repo = _repositoryList?.find(item => item?.metadata?.name === selectedRepo);

const registry = `${repoBaseUrl}/${repo?.spec?.namespaceName}/${repo?.spec?.name}`;
onConfirm({ registry, tags: repo?.status?.tags ?? null });
setVisible(false);
}}
>
确认
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { Button, Modal, Table, Checkbox, TableColumn, Text } from 'tea-component';

const { scrollable, radioable } = Table.addons;

export const RegistryTagSelectDialog = ({ tags, onConfirm }) => {
const [visible, setVisible] = useState(false);

const [selectedTag, setSelectedTag] = useState(null);

const columns: TableColumn[] = [
{
key: 'name',
header: '镜像版本'
},

{
key: 'digest',
header: '摘要(SHA256)',
render({ digest }) {
return <Text copyable>{digest}</Text>;
}
}
];

return (
<>
<Button type="link" onClick={() => setVisible(true)}>
选择镜像版本
</Button>

<Modal caption="选择镜像版本" visible={visible} onClose={() => setVisible(false)}>
<Modal.Body>
<Table
columns={columns}
records={tags}
recordKey="name"
addons={[
scrollable({
maxHeight: 500
}),

radioable({
value: selectedTag,
onChange(key) {
setSelectedTag(key);
},
rowSelect: true
})
]}
/>
</Modal.Body>

<Modal.Footer>
<Button
disabled={!selectedTag}
onClick={() => {
onConfirm(selectedTag);
setVisible(false);
}}
type="primary"
>
确认
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useState } from 'react';
import { Button, Modal, Form, TextArea } from 'tea-component';
import { registryApi } from '@src/webApi';

interface INamespaceDisplayNameEditorProps {
value: string;
name: string;
onSuccess: () => void;
}

export const NamespaceDisplayNameEditor = ({ value, name, onSuccess }: INamespaceDisplayNameEditorProps) => {
const [visible, _setVisible] = useState(false);

function setVisible(visible: boolean) {
_setVisible(visible);

setDisplayName(value);
}

const [displayName, setDisplayName] = useState(value);

async function handleSubmit() {
await registryApi.modifyNamespaceDisplayName({ name, displayName });

onSuccess();

setVisible(false);
}

return (
<>
<Button type="icon" icon="pencil" onClick={() => setVisible(true)} />

<Modal visible={visible} caption="编辑描述信息" onClose={() => setVisible(false)}>
<Modal.Body>
<Form>
<Form.Item label="描述" required>
<TextArea size="full" value={displayName} onChange={value => setDisplayName(value.trim())} />
</Form.Item>
</Form>
</Modal.Body>

<Modal.Footer>
<Button type="primary" disabled={!displayName} onClick={handleSubmit}>
确定
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DialogBodyLayout } from '../../../common/layouts';
import { Repo } from '../../models';
import { router } from '../../router';
import { RootProps } from '../RegistryApp';
import { NamespaceDisplayNameEditor } from './NamespaceDisplayNameEditor';

export class RepoTablePanel extends React.Component<RootProps, any> {
componentDidMount() {
Expand Down Expand Up @@ -96,6 +97,11 @@ export class RepoTablePanel extends React.Component<RootProps, any> {
render: (x: Repo) => (
<Text parent="div" overflow>
<span className="text">{x.spec.displayName}</span>
<NamespaceDisplayNameEditor
value={x?.spec?.displayName ?? ''}
name={x?.metadata?.name}
onSuccess={() => this.props.actions.repo.fetch()}
/>
</Text>
)
},
Expand Down
2 changes: 2 additions & 0 deletions web/console/src/webApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * as virtualMachineAPI from './virtual-machine';
export * as storageClassAPI from './storage-class';

export * as PVCAPI from './pvc';

export * as registryApi from './registry';
Loading

0 comments on commit 4945e11

Please sign in to comment.