import React, { memo, useState } from 'react'; import { ServerEggVariable } from '@/api/server/types'; import TitledGreyBox from '@/components/elements/TitledGreyBox'; import { usePermissions } from '@/plugins/usePermissions'; import InputSpinner from '@/components/elements/InputSpinner'; import Input from '@/components/elements/Input'; import Switch from '@/components/elements/Switch'; import tw from 'twin.macro'; import { debounce } from 'debounce'; import updateStartupVariable from '@/api/server/updateStartupVariable'; import useFlash from '@/plugins/useFlash'; import FlashMessageRender from '@/components/FlashMessageRender'; import getServerStartup from '@/api/swr/getServerStartup'; import Select from '@/components/elements/Select'; import isEqual from 'react-fast-compare'; import { ServerContext } from '@/state/server'; interface Props { variable: ServerEggVariable; } const VariableBox = ({ variable }: Props) => { const FLASH_KEY = `server:startup:${variable.envVariable}`; const uuid = ServerContext.useStoreState(state => state.server.data!.uuid); const [ loading, setLoading ] = useState(false); const [ canEdit ] = usePermissions([ 'startup.update' ]); const { clearFlashes, clearAndAddHttpError } = useFlash(); const { mutate } = getServerStartup(uuid); const setVariableValue = debounce((value: string) => { setLoading(true); clearFlashes(FLASH_KEY); updateStartupVariable(uuid, variable.envVariable, value) .then(([ response, invocation ]) => mutate(data => ({ ...data!, invocation, variables: (data!.variables || []).map(v => v.envVariable === response.envVariable ? response : v), }), false)) .catch(error => { console.error(error); clearAndAddHttpError({ key: FLASH_KEY, error }); }) .then(() => setLoading(false)); }, 500); const useSwitch = variable.rules.some(v => v === 'boolean' || v === 'in:0,1'); const selectValues = variable.rules.find(v => v.startsWith('in:'))?.split(',') || []; return ( <TitledGreyBox title={ <p css={tw`text-sm uppercase`}> {!variable.isEditable && <span css={tw`bg-neutral-700 text-xs py-1 px-2 rounded-full mr-2 mb-1`}>Read Only</span> } {variable.name} </p> } > <FlashMessageRender byKey={FLASH_KEY} css={tw`mb-2 md:mb-4`}/> <InputSpinner visible={loading}> {useSwitch ? <> <Switch readOnly={!canEdit || !variable.isEditable} name={variable.envVariable} defaultChecked={variable.serverValue === '1'} onChange={() => { if (canEdit && variable.isEditable) { setVariableValue(variable.serverValue === '1' ? '0' : '1'); } }} /> </> : <> {selectValues.length > 0 ? <> <Select onChange={e => setVariableValue(e.target.value)} name={variable.envVariable} defaultValue={variable.serverValue} disabled={!canEdit || !variable.isEditable} > {selectValues.map(selectValue => ( <option key={selectValue.replace('in:', '')} value={selectValue.replace('in:', '')}>{selectValue.replace('in:', '')}</option> ))} </Select> </> : <> <Input onKeyUp={e => { if (canEdit && variable.isEditable) { setVariableValue(e.currentTarget.value); } }} readOnly={!canEdit || !variable.isEditable} name={variable.envVariable} defaultValue={variable.serverValue} placeholder={variable.defaultValue} /> </> } </> } </InputSpinner> <p css={tw`mt-1 text-xs text-neutral-300`}> {variable.description} </p> </TitledGreyBox> ); }; export default memo(VariableBox, isEqual);