ui(admin): add startup and file configuration editing for eggs
This commit is contained in:
parent
7d1cb2971f
commit
b125830859
7 changed files with 112 additions and 40 deletions
|
@ -40,6 +40,8 @@
|
||||||
"@codemirror/theme-one-dark": "^0.19.0",
|
"@codemirror/theme-one-dark": "^0.19.0",
|
||||||
"@codemirror/view": "^0.19.4",
|
"@codemirror/view": "^0.19.4",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.15",
|
"@fortawesome/react-fontawesome": "^0.1.15",
|
||||||
"@hot-loader/react-dom": "^16.14.0",
|
"@hot-loader/react-dom": "^16.14.0",
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import http from '@/api/http';
|
import http from '@/api/http';
|
||||||
import { Egg, rawDataToEgg } from '@/api/admin/eggs/getEgg';
|
import { Egg, rawDataToEgg } from '@/api/admin/eggs/getEgg';
|
||||||
|
|
||||||
export default (id: number, egg: Partial<Egg>): Promise<Egg> => {
|
type Egg2 = Omit<Omit<Partial<Egg>, 'configFiles'>, 'configStartup'> & { configFiles?: string, configStartup?: string };
|
||||||
|
|
||||||
|
export default (id: number, egg: Partial<Egg2>): Promise<Egg> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.patch(
|
http.patch(
|
||||||
`/api/application/eggs/${id}`,
|
`/api/application/eggs/${id}`,
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default ({ className }: { className?: string }) => {
|
||||||
const match = useRouteMatch<{ nestId: string }>();
|
const match = useRouteMatch<{ nestId: string }>();
|
||||||
const { mutate } = getEggs(Number(match.params.nestId));
|
const { mutate } = getEggs(Number(match.params.nestId));
|
||||||
|
|
||||||
let fetchFileContent: null | (() => Promise<string>) = null;
|
let fetchFileContent: (() => Promise<string>) | null = null;
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
clearFlashes('egg:import');
|
clearFlashes('egg:import');
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface Values {
|
||||||
export default function EggInstallContainer ({ egg }: { egg: Egg }) {
|
export default function EggInstallContainer ({ egg }: { egg: Egg }) {
|
||||||
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||||
|
|
||||||
let fetchFileContent: null | (() => Promise<string>) = null;
|
let fetchFileContent: (() => Promise<string>) | null = null;
|
||||||
|
|
||||||
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
if (fetchFileContent === null) {
|
if (fetchFileContent === null) {
|
||||||
|
|
|
@ -8,8 +8,9 @@ import Label from '@/components/elements/Label';
|
||||||
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
|
||||||
import useFlash from '@/plugins/useFlash';
|
import useFlash from '@/plugins/useFlash';
|
||||||
import { jsonLanguage } from '@codemirror/lang-json';
|
import { jsonLanguage } from '@codemirror/lang-json';
|
||||||
import { faEgg, faTerminal } from '@fortawesome/free-solid-svg-icons';
|
import { faDocker } from '@fortawesome/free-brands-svg-icons';
|
||||||
import React from 'react';
|
import { faEgg, faFireAlt, faMicrochip, faTerminal } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import AdminBox from '@/components/admin/AdminBox';
|
import AdminBox from '@/components/admin/AdminBox';
|
||||||
import { Egg } from '@/api/admin/eggs/getEgg';
|
import { Egg } from '@/api/admin/eggs/getEgg';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
@ -93,7 +94,7 @@ function EggImageContainer () {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox icon={undefined} title={'Image'} css={tw`relative`}>
|
<AdminBox icon={faDocker} title={'Docker'} css={tw`relative`}>
|
||||||
<SpinnerOverlay visible={isSubmitting}/>
|
<SpinnerOverlay visible={isSubmitting}/>
|
||||||
|
|
||||||
<TextareaField
|
<TextareaField
|
||||||
|
@ -106,11 +107,11 @@ function EggImageContainer () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EggStopContainer () {
|
function EggLifecycleContainer () {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox icon={undefined} title={'Stop'} css={tw`relative`}>
|
<AdminBox icon={faFireAlt} title={'Lifecycle'} css={tw`relative`}>
|
||||||
<SpinnerOverlay visible={isSubmitting}/>
|
<SpinnerOverlay visible={isSubmitting}/>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
@ -124,11 +125,41 @@ function EggStopContainer () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EggProcessContainer ({ className, egg }: { className?: string, egg: Egg }) {
|
interface EggProcessContainerProps {
|
||||||
|
className?: string;
|
||||||
|
egg: Egg;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EggProcessContainerRef {
|
||||||
|
getStartupConfiguration: () => Promise<string | null>;
|
||||||
|
getFilesConfiguration: () => Promise<string | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EggProcessContainer = forwardRef<any, EggProcessContainerProps>(
|
||||||
|
function EggProcessContainer ({ className, egg }, ref) {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
|
let fetchStartupConfiguration: (() => Promise<string>) | null = null;
|
||||||
|
let fetchFilesConfiguration: (() => Promise<string>) | null = null;
|
||||||
|
|
||||||
|
useImperativeHandle<EggProcessContainerRef, EggProcessContainerRef>(ref, () => ({
|
||||||
|
getStartupConfiguration: async () => {
|
||||||
|
if (fetchStartupConfiguration === null) {
|
||||||
|
return new Promise<null>(resolve => resolve(null));
|
||||||
|
}
|
||||||
|
return await fetchStartupConfiguration();
|
||||||
|
},
|
||||||
|
|
||||||
|
getFilesConfiguration: async () => {
|
||||||
|
if (fetchFilesConfiguration === null) {
|
||||||
|
return new Promise<null>(resolve => resolve(null));
|
||||||
|
}
|
||||||
|
return await fetchFilesConfiguration();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminBox title={'Process Configuration'} css={tw`relative`} className={className}>
|
<AdminBox icon={faMicrochip} title={'Process Configuration'} css={tw`relative`} className={className}>
|
||||||
<SpinnerOverlay visible={isSubmitting}/>
|
<SpinnerOverlay visible={isSubmitting}/>
|
||||||
|
|
||||||
<div css={tw`mb-6`}>
|
<div css={tw`mb-6`}>
|
||||||
|
@ -137,6 +168,9 @@ function EggProcessContainer ({ className, egg }: { className?: string, egg: Egg
|
||||||
mode={jsonLanguage}
|
mode={jsonLanguage}
|
||||||
initialContent={JSON.stringify(egg.configStartup, null, '\t') || ''}
|
initialContent={JSON.stringify(egg.configStartup, null, '\t') || ''}
|
||||||
overrides={tw`h-32 rounded`}
|
overrides={tw`h-32 rounded`}
|
||||||
|
fetchContent={value => {
|
||||||
|
fetchStartupConfiguration = value;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -146,11 +180,15 @@ function EggProcessContainer ({ className, egg }: { className?: string, egg: Egg
|
||||||
mode={jsonLanguage}
|
mode={jsonLanguage}
|
||||||
initialContent={JSON.stringify(egg.configFiles, null, '\t') || ''}
|
initialContent={JSON.stringify(egg.configFiles, null, '\t') || ''}
|
||||||
overrides={tw`h-48 rounded`}
|
overrides={tw`h-48 rounded`}
|
||||||
|
fetchContent={value => {
|
||||||
|
fetchFilesConfiguration = value;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AdminBox>
|
</AdminBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
interface Values {
|
interface Values {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -158,6 +196,8 @@ interface Values {
|
||||||
startup: string;
|
startup: string;
|
||||||
dockerImages: string;
|
dockerImages: string;
|
||||||
stopCommand: string;
|
stopCommand: string;
|
||||||
|
configStartup: string;
|
||||||
|
configFiles: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
|
@ -165,10 +205,13 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
|
|
||||||
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||||
|
|
||||||
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
const ref = useRef<EggProcessContainerRef>();
|
||||||
|
|
||||||
|
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
clearFlashes('egg');
|
clearFlashes('egg');
|
||||||
|
|
||||||
// TODO: Send data from code blocks.
|
values.configStartup = await ref.current?.getStartupConfiguration() || '';
|
||||||
|
values.configFiles = await ref.current?.getFilesConfiguration() || '';
|
||||||
|
|
||||||
updateEgg(egg.id, { ...values, dockerImages: values.dockerImages.split('\n') })
|
updateEgg(egg.id, { ...values, dockerImages: values.dockerImages.split('\n') })
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -187,6 +230,8 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
startup: egg.startup,
|
startup: egg.startup,
|
||||||
dockerImages: egg.dockerImages.join('\n'),
|
dockerImages: egg.dockerImages.join('\n'),
|
||||||
stopCommand: egg.configStop || '',
|
stopCommand: egg.configStop || '',
|
||||||
|
configStartup: '',
|
||||||
|
configFiles: '',
|
||||||
}}
|
}}
|
||||||
validationSchema={object().shape({
|
validationSchema={object().shape({
|
||||||
})}
|
})}
|
||||||
|
@ -202,10 +247,14 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
|
|
||||||
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}>
|
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}>
|
||||||
<EggImageContainer/>
|
<EggImageContainer/>
|
||||||
<EggStopContainer/>
|
<EggLifecycleContainer/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EggProcessContainer egg={egg} css={tw`mb-6`}/>
|
<EggProcessContainer
|
||||||
|
ref={ref}
|
||||||
|
egg={egg}
|
||||||
|
css={tw`mb-6`}
|
||||||
|
/>
|
||||||
|
|
||||||
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-6 mb-16`}>
|
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-6 mb-16`}>
|
||||||
<div css={tw`flex flex-row`}>
|
<div css={tw`flex flex-row`}>
|
||||||
|
|
|
@ -36,8 +36,7 @@ export default () => {
|
||||||
const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory);
|
const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory);
|
||||||
const { addError, clearFlashes } = useFlash();
|
const { addError, clearFlashes } = useFlash();
|
||||||
|
|
||||||
// eslint-disable-next-line prefer-const
|
let fetchFileContent: (() => Promise<string>) | null = null;
|
||||||
let fetchFileContent: null | (() => Promise<string>) = null;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (action === 'new') return;
|
if (action === 'new') return;
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -2178,6 +2178,24 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@fortawesome/free-brands-svg-icons@npm:^5.15.4":
|
||||||
|
version: 5.15.4
|
||||||
|
resolution: "@fortawesome/free-brands-svg-icons@npm:5.15.4"
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-common-types": ^0.2.36
|
||||||
|
checksum: 06e38132fbdf04d8677cf6e47e73cd566f68256f542d68d354e26f5ca7536c714b56f5f3dc6c670065b888ee08bc913bb4f213ab08225faf955d893a6a86ed02
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@fortawesome/free-regular-svg-icons@npm:^5.15.4":
|
||||||
|
version: 5.15.4
|
||||||
|
resolution: "@fortawesome/free-regular-svg-icons@npm:5.15.4"
|
||||||
|
dependencies:
|
||||||
|
"@fortawesome/fontawesome-common-types": ^0.2.36
|
||||||
|
checksum: 2e6039e3bb2125940ed2cb5738b6562b082755c1e45b73571ee92d976773ab81118c9efb9a7b57453b664a025613e81e1dafd2235aafeaadd8f0d75f8e1fe25e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@fortawesome/free-solid-svg-icons@npm:^5.15.4":
|
"@fortawesome/free-solid-svg-icons@npm:^5.15.4":
|
||||||
version: 5.15.4
|
version: 5.15.4
|
||||||
resolution: "@fortawesome/free-solid-svg-icons@npm:5.15.4"
|
resolution: "@fortawesome/free-solid-svg-icons@npm:5.15.4"
|
||||||
|
@ -9923,6 +9941,8 @@ fsevents@^1.2.7:
|
||||||
"@codemirror/theme-one-dark": ^0.19.0
|
"@codemirror/theme-one-dark": ^0.19.0
|
||||||
"@codemirror/view": ^0.19.4
|
"@codemirror/view": ^0.19.4
|
||||||
"@fortawesome/fontawesome-svg-core": ^1.2.36
|
"@fortawesome/fontawesome-svg-core": ^1.2.36
|
||||||
|
"@fortawesome/free-brands-svg-icons": ^5.15.4
|
||||||
|
"@fortawesome/free-regular-svg-icons": ^5.15.4
|
||||||
"@fortawesome/free-solid-svg-icons": ^5.15.4
|
"@fortawesome/free-solid-svg-icons": ^5.15.4
|
||||||
"@fortawesome/react-fontawesome": ^0.1.15
|
"@fortawesome/react-fontawesome": ^0.1.15
|
||||||
"@hot-loader/react-dom": ^16.14.0
|
"@hot-loader/react-dom": ^16.14.0
|
||||||
|
|
Loading…
Reference in a new issue