mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 18:53:06 +00:00
690 lines
22 KiB
JavaScript
690 lines
22 KiB
JavaScript
import { toTitleCase } from 'common/string';
|
|
import { Fragment } from 'inferno';
|
|
import { useBackend, useLocalState, useSharedState } from '../backend';
|
|
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Tabs, Input, NumberInput, Table, Divider } from '../components';
|
|
import { Window } from '../layouts';
|
|
|
|
const ResearchConsoleViewResearch = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { tech } = data;
|
|
|
|
return (
|
|
<Section
|
|
title="Current Research Levels"
|
|
buttons={
|
|
<Button icon="print" onClick={() => act('print', { print: 1 })}>
|
|
Print This Page
|
|
</Button>
|
|
}>
|
|
<Table>
|
|
{tech.map((thing) => (
|
|
<Table.Row key={thing.name}>
|
|
<Table.Cell>
|
|
<Box color="label">{thing.name}</Box>
|
|
<Box> - Level {thing.level}</Box>
|
|
</Table.Cell>
|
|
<Table.Cell>
|
|
<Box color="label">{thing.desc}</Box>
|
|
</Table.Cell>
|
|
</Table.Row>
|
|
))}
|
|
</Table>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const PaginationTitle = (props, context) => {
|
|
const { data } = useBackend(context);
|
|
|
|
const { title, target } = props;
|
|
|
|
let page = data[target];
|
|
if (typeof page === 'number') {
|
|
return title + ' - Page ' + (page + 1);
|
|
}
|
|
|
|
return title;
|
|
};
|
|
|
|
const PaginationChevrons = (props, context) => {
|
|
const { act } = useBackend(context);
|
|
|
|
const { target } = props;
|
|
|
|
return (
|
|
<Fragment>
|
|
<Button icon="undo" onClick={() => act(target, { reset: true })} />
|
|
<Button icon="chevron-left" onClick={() => act(target, { reverse: -1 })} />
|
|
<Button icon="chevron-right" onClick={() => act(target, { reverse: 1 })} />
|
|
</Fragment>
|
|
);
|
|
};
|
|
|
|
const ResearchConsoleViewDesigns = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { designs } = data;
|
|
|
|
return (
|
|
<Section
|
|
title={<PaginationTitle title="Researched Technologies & Designs" target="design_page" />}
|
|
buttons={
|
|
<Fragment>
|
|
<Button icon="print" onClick={() => act('print', { print: 2 })}>
|
|
Print This Page
|
|
</Button>
|
|
{<PaginationChevrons target={'design_page'} /> || null}
|
|
</Fragment>
|
|
}>
|
|
<Input
|
|
fluid
|
|
placeholder="Search for..."
|
|
value={data.search}
|
|
onInput={(e, v) => act('search', { search: v })}
|
|
mb={1}
|
|
/>
|
|
{(designs && designs.length && (
|
|
<LabeledList>
|
|
{designs.map((design) => (
|
|
<LabeledList.Item label={design.name} key={design.name}>
|
|
{design.desc}
|
|
</LabeledList.Item>
|
|
))}
|
|
</LabeledList>
|
|
)) || <Box color="warning">No designs found.</Box>}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const TechDisk = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { tech } = data;
|
|
|
|
const { disk } = props;
|
|
|
|
if (!disk || !disk.present) {
|
|
return null;
|
|
}
|
|
|
|
const [saveDialog, setSaveDialog] = useSharedState(context, 'saveDialogTech', false);
|
|
|
|
if (saveDialog) {
|
|
return (
|
|
<Section
|
|
title="Load Technology to Disk"
|
|
buttons={<Button icon="arrow-left" content="Back" onClick={() => setSaveDialog(false)} />}>
|
|
<LabeledList>
|
|
{tech.map((level) => (
|
|
<LabeledList.Item label={level.name} key={level.name}>
|
|
<Button
|
|
icon="save"
|
|
onClick={() => {
|
|
setSaveDialog(false);
|
|
act('copy_tech', { copy_tech_ID: level.id });
|
|
}}>
|
|
Copy To Disk
|
|
</Button>
|
|
</LabeledList.Item>
|
|
))}
|
|
</LabeledList>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Disk Contents">(Technology Data Disk)</LabeledList.Item>
|
|
</LabeledList>
|
|
{(disk.stored && (
|
|
<Box mt={2}>
|
|
<Box>{disk.name}</Box>
|
|
<Box>Level: {disk.level}</Box>
|
|
<Box>Description: {disk.desc}</Box>
|
|
<Box mt={1}>
|
|
<Button icon="save" onClick={() => act('updt_tech')}>
|
|
Upload to Database
|
|
</Button>
|
|
<Button icon="trash" onClick={() => act('clear_tech')}>
|
|
Clear Disk
|
|
</Button>
|
|
<Button icon="eject" onClick={() => act('eject_tech')}>
|
|
Eject Disk
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
)) || (
|
|
<Box>
|
|
<Box>This disk has no data stored on it.</Box>
|
|
<Button icon="save" onClick={() => setSaveDialog(true)}>
|
|
Load Tech To Disk
|
|
</Button>
|
|
<Button icon="eject" onClick={() => act('eject_tech')}>
|
|
Eject Disk
|
|
</Button>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const DataDisk = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { designs } = data.info;
|
|
|
|
const { disk } = props;
|
|
|
|
if (!disk || !disk.present) {
|
|
return null;
|
|
}
|
|
|
|
const [saveDialog, setSaveDialog] = useSharedState(context, 'saveDialogData', false);
|
|
|
|
if (saveDialog) {
|
|
return (
|
|
<Section
|
|
title={<PaginationTitle title="Load Design to Disk" target="design_page" />}
|
|
buttons={
|
|
<Fragment>
|
|
<Button icon="arrow-left" content="Back" onClick={() => setSaveDialog(false)} />
|
|
{<PaginationChevrons target={'design_page'} /> || null}
|
|
</Fragment>
|
|
}>
|
|
<Input
|
|
fluid
|
|
placeholder="Search for..."
|
|
value={data.search}
|
|
onInput={(e, v) => act('search', { search: v })}
|
|
mb={1}
|
|
/>
|
|
<LabeledList>
|
|
{designs.map((item) => (
|
|
<LabeledList.Item label={item.name} key={item.name}>
|
|
<Button
|
|
icon="save"
|
|
onClick={() => {
|
|
setSaveDialog(false);
|
|
act('copy_design', { copy_design_ID: item.id });
|
|
}}>
|
|
Copy To Disk
|
|
</Button>
|
|
</LabeledList.Item>
|
|
))}
|
|
</LabeledList>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box>
|
|
{(disk.stored && (
|
|
<Box>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Name">{disk.name}</LabeledList.Item>
|
|
<LabeledList.Item label="Lathe Type">{disk.build_type}</LabeledList.Item>
|
|
<LabeledList.Item label="Required Materials">
|
|
{Object.keys(disk.materials).map((mat) => (
|
|
<Box key={mat}>
|
|
{mat} x {disk.materials[mat]}
|
|
</Box>
|
|
))}
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Box mt={1}>
|
|
<Button icon="save" onClick={() => act('updt_design')}>
|
|
Upload to Database
|
|
</Button>
|
|
<Button icon="trash" onClick={() => act('clear_design')}>
|
|
Clear Disk
|
|
</Button>
|
|
<Button icon="eject" onClick={() => act('eject_design')}>
|
|
Eject Disk
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
)) || (
|
|
<Box>
|
|
<Box mb={0.5}>This disk has no data stored on it.</Box>
|
|
<Button icon="save" onClick={() => setSaveDialog(true)}>
|
|
Load Design To Disk
|
|
</Button>
|
|
<Button icon="eject" onClick={() => act('eject_design')}>
|
|
Eject Disk
|
|
</Button>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const ResearchConsoleDisk = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { d_disk, t_disk } = data.info;
|
|
|
|
if (!d_disk.present && !t_disk.present) {
|
|
return <Section title="Disk Operations">No disk inserted.</Section>;
|
|
}
|
|
|
|
return (
|
|
<Section title="Disk Operations">
|
|
<TechDisk disk={t_disk} />
|
|
<DataDisk disk={d_disk} />
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const ResearchConsoleDestructiveAnalyzer = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { linked_destroy } = data.info;
|
|
|
|
if (!linked_destroy.present) {
|
|
return <Section title="Destructive Analyzer">No destructive analyzer found.</Section>;
|
|
}
|
|
|
|
const { loaded_item, origin_tech } = linked_destroy;
|
|
|
|
return (
|
|
<Section title="Destructive Analyzer">
|
|
{(loaded_item && (
|
|
<Box>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Name">{loaded_item}</LabeledList.Item>
|
|
<LabeledList.Item label="Origin Tech">
|
|
<LabeledList>
|
|
{(origin_tech.length &&
|
|
origin_tech.map((tech) => (
|
|
<LabeledList.Item label={tech.name} key={tech.name}>
|
|
{tech.level} {tech.current && '(Current: ' + tech.current + ')'}
|
|
</LabeledList.Item>
|
|
))) || <LabeledList.Item label="Error">No origin tech found.</LabeledList.Item>}
|
|
</LabeledList>
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Button mt={1} color="red" icon="eraser" onClick={() => act('deconstruct')}>
|
|
Deconstruct Item
|
|
</Button>
|
|
<Button icon="eject" onClick={() => act('eject_item')}>
|
|
Eject Item
|
|
</Button>
|
|
</Box>
|
|
)) || <Box>No Item Loaded. Standing-by...</Box>}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const ResearchConsoleBuildMenu = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { target, designs, buildName, buildFiveName } = props;
|
|
|
|
if (!target) {
|
|
return <Box color="bad">Error</Box>;
|
|
}
|
|
|
|
return (
|
|
<Section
|
|
title={<PaginationTitle target="builder_page" title="Designs" />}
|
|
buttons={<PaginationChevrons target={'builder_page'} />}>
|
|
<Input
|
|
fluid
|
|
placeholder="Search for..."
|
|
value={data.search}
|
|
onInput={(e, v) => act('search', { search: v })}
|
|
mb={1}
|
|
/>
|
|
{designs && designs.length ? (
|
|
designs.map((design) => (
|
|
<Fragment key={design.id}>
|
|
<Flex width="100%" justify="space-between">
|
|
<Flex.Item width="40%" style={{ 'word-wrap': 'break-all' }}>
|
|
{design.name}
|
|
</Flex.Item>
|
|
<Flex.Item width="15%" textAlign="center">
|
|
<Button mb={-1} icon="wrench" onClick={() => act(buildName, { build: design.id, imprint: design.id })}>
|
|
Build
|
|
</Button>
|
|
{buildFiveName && (
|
|
<Button mb={-1} onClick={() => act(buildFiveName, { build: design.id, imprint: design.id })}>
|
|
x5
|
|
</Button>
|
|
)}
|
|
</Flex.Item>
|
|
<Flex.Item width="45%" style={{ 'word-wrap': 'break-all' }}>
|
|
<Box inline color="label">
|
|
{design.mat_list.join(' ')}
|
|
</Box>
|
|
<Box inline color="average" ml={1}>
|
|
{design.chem_list.join(' ')}
|
|
</Box>
|
|
</Flex.Item>
|
|
</Flex>
|
|
<Divider />
|
|
</Fragment>
|
|
))
|
|
) : (
|
|
<Box>No items could be found matching the parameters (page or search).</Box>
|
|
)}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
/* Lathe + Circuit Imprinter all in one */
|
|
const ResearchConsoleConstructor = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { name } = props;
|
|
|
|
let linked = null;
|
|
let designs = null;
|
|
|
|
if (name === 'Protolathe') {
|
|
linked = data.info.linked_lathe;
|
|
designs = data.lathe_designs;
|
|
} else {
|
|
linked = data.info.linked_imprinter;
|
|
designs = data.imprinter_designs;
|
|
}
|
|
|
|
if (!linked || !linked.present) {
|
|
return <Section title={name}>No {name} found.</Section>;
|
|
}
|
|
|
|
const { total_materials, max_materials, total_volume, max_volume, busy, mats, reagents, queue } = linked;
|
|
|
|
const [protoTab, setProtoTab] = useSharedState(context, 'protoTab', 0);
|
|
|
|
let queueColor = 'transparent';
|
|
let queueSpin = false;
|
|
let queueIcon = 'layer-group';
|
|
if (busy) {
|
|
queueIcon = 'hammer';
|
|
queueColor = 'average';
|
|
queueSpin = true;
|
|
} else if (queue && queue.length) {
|
|
queueIcon = 'sync';
|
|
queueColor = 'green';
|
|
queueSpin = true;
|
|
}
|
|
|
|
// Proto vs Circuit differences
|
|
let removeQueueAction = name === 'Protolathe' ? 'removeP' : 'removeI';
|
|
let ejectSheetAction = name === 'Protolathe' ? 'lathe_ejectsheet' : 'imprinter_ejectsheet';
|
|
let ejectChemAction = name === 'Protolathe' ? 'disposeP' : 'disposeI';
|
|
let ejectAllChemAction = name === 'Protolathe' ? 'disposeallP' : 'disposeallI';
|
|
|
|
return (
|
|
<Section title={name} buttons={(busy && <Icon name="sync" spin />) || null}>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Materials">
|
|
<ProgressBar value={total_materials} maxValue={max_materials}>
|
|
{total_materials} cm³ / {max_materials} cm³
|
|
</ProgressBar>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Chemicals">
|
|
<ProgressBar value={total_volume} maxValue={max_volume}>
|
|
{total_volume}u / {max_volume}u
|
|
</ProgressBar>
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Tabs mt={1}>
|
|
<Tabs.Tab icon="wrench" selected={protoTab === 0} onClick={() => setProtoTab(0)}>
|
|
Build
|
|
</Tabs.Tab>
|
|
<Tabs.Tab
|
|
icon={queueIcon}
|
|
iconSpin={queueSpin}
|
|
color={queueColor}
|
|
selected={protoTab === 1}
|
|
onClick={() => setProtoTab(1)}>
|
|
Queue
|
|
</Tabs.Tab>
|
|
<Tabs.Tab icon="cookie-bite" selected={protoTab === 2} onClick={() => setProtoTab(2)}>
|
|
Mat Storage
|
|
</Tabs.Tab>
|
|
<Tabs.Tab icon="flask" selected={protoTab === 3} onClick={() => setProtoTab(3)}>
|
|
Chem Storage
|
|
</Tabs.Tab>
|
|
</Tabs>
|
|
{(protoTab === 0 && (
|
|
<ResearchConsoleBuildMenu
|
|
target={linked}
|
|
designs={designs}
|
|
buildName={name === 'Protolathe' ? 'build' : 'imprint'}
|
|
buildFiveName={name === 'Protolathe' ? 'buildfive' : null}
|
|
/>
|
|
)) ||
|
|
(protoTab === 1 && (
|
|
<LabeledList>
|
|
{(queue.length &&
|
|
queue.map((item) => {
|
|
if (item.index === 1) {
|
|
return (
|
|
<LabeledList.Item label={item.name} labelColor="bad">
|
|
{!busy ? (
|
|
<Box>
|
|
(Awaiting Materials)
|
|
<Button
|
|
ml={1}
|
|
icon="trash"
|
|
onClick={() => act(removeQueueAction, { [removeQueueAction]: item.index })}>
|
|
Remove
|
|
</Button>
|
|
</Box>
|
|
) : (
|
|
<Button disabled icon="trash">
|
|
Remove
|
|
</Button>
|
|
)}
|
|
</LabeledList.Item>
|
|
);
|
|
}
|
|
return (
|
|
<LabeledList.Item label={item.name} key={item.name}>
|
|
<Button icon="trash" onClick={() => act(removeQueueAction, { [removeQueueAction]: item.index })}>
|
|
Remove
|
|
</Button>
|
|
</LabeledList.Item>
|
|
);
|
|
})) || <Box m={1}>Queue Empty.</Box>}
|
|
</LabeledList>
|
|
)) ||
|
|
(protoTab === 2 && (
|
|
<LabeledList>
|
|
{mats.map((mat) => {
|
|
const [ejectAmt, setEjectAmt] = useLocalState(context, 'ejectAmt' + mat.name, 0);
|
|
return (
|
|
<LabeledList.Item
|
|
label={toTitleCase(mat.name)}
|
|
key={mat.name}
|
|
buttons={
|
|
<Fragment>
|
|
<NumberInput
|
|
minValue={0}
|
|
width="100px"
|
|
value={ejectAmt}
|
|
maxValue={mat.sheets}
|
|
onDrag={(e, val) => setEjectAmt(val)}
|
|
/>
|
|
<Button
|
|
icon="eject"
|
|
disabled={!mat.removable}
|
|
onClick={() => {
|
|
setEjectAmt(0);
|
|
act(ejectSheetAction, { [ejectSheetAction]: mat.name, amount: ejectAmt });
|
|
}}>
|
|
Num
|
|
</Button>
|
|
<Button
|
|
icon="eject"
|
|
disabled={!mat.removable}
|
|
onClick={() => act(ejectSheetAction, { [ejectSheetAction]: mat.name, amount: 50 })}>
|
|
All
|
|
</Button>
|
|
</Fragment>
|
|
}>
|
|
{mat.amount} cm³
|
|
</LabeledList.Item>
|
|
);
|
|
})}
|
|
</LabeledList>
|
|
)) ||
|
|
(protoTab === 3 && (
|
|
<Box>
|
|
<LabeledList>
|
|
{(reagents.length &&
|
|
reagents.map((chem) => (
|
|
<LabeledList.Item label={chem.name} key={chem.name}>
|
|
{chem.volume}u
|
|
<Button ml={1} icon="eject" onClick={() => act(ejectChemAction, { dispose: chem.id })}>
|
|
Purge
|
|
</Button>
|
|
</LabeledList.Item>
|
|
))) || <LabeledList.Item label="Empty">No chems detected</LabeledList.Item>}
|
|
</LabeledList>
|
|
<Button mt={1} icon="trash" onClick={() => act(ejectAllChemAction)}>
|
|
Disposal All Chemicals In Storage
|
|
</Button>
|
|
</Box>
|
|
)) || <Box>Error</Box>}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const ResearchConsoleSettings = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { sync, linked_destroy, linked_imprinter, linked_lathe } = data.info;
|
|
|
|
const [settingsTab, setSettingsTab] = useSharedState(context, 'settingsTab', 0);
|
|
|
|
return (
|
|
<Section title="Settings">
|
|
<Tabs>
|
|
<Tabs.Tab icon="cogs" onClick={() => setSettingsTab(0)} selected={settingsTab === 0}>
|
|
General
|
|
</Tabs.Tab>
|
|
<Tabs.Tab icon="link" onClick={() => setSettingsTab(1)} selected={settingsTab === 1}>
|
|
Device Linkages
|
|
</Tabs.Tab>
|
|
</Tabs>
|
|
{(settingsTab === 0 && (
|
|
<Box>
|
|
{(sync && (
|
|
<Fragment>
|
|
<Button fluid icon="sync" onClick={() => act('sync')}>
|
|
Sync Database with Network
|
|
</Button>
|
|
<Button fluid icon="unlink" onClick={() => act('togglesync')}>
|
|
Disconnect from Research Network
|
|
</Button>
|
|
</Fragment>
|
|
)) || (
|
|
<Button fluid icon="link" onClick={() => act('togglesync')}>
|
|
Connect to Research Network
|
|
</Button>
|
|
)}
|
|
<Button fluid icon="lock" onClick={() => act('lock')}>
|
|
Lock Console
|
|
</Button>
|
|
<Button fluid color="red" icon="trash" onClick={() => act('reset')}>
|
|
Reset R&D Database
|
|
</Button>
|
|
</Box>
|
|
)) ||
|
|
(settingsTab === 1 && (
|
|
<Box>
|
|
<Button fluid icon="sync" mb={1} onClick={() => act('find_device')}>
|
|
Re-sync with Nearby Devices
|
|
</Button>
|
|
<LabeledList>
|
|
{(linked_destroy.present && (
|
|
<LabeledList.Item label="Destructive Analyzer">
|
|
<Button icon="unlink" onClick={() => act('disconnect', { disconnect: 'destroy' })}>
|
|
Disconnect
|
|
</Button>
|
|
</LabeledList.Item>
|
|
)) ||
|
|
null}
|
|
{(linked_lathe.present && (
|
|
<LabeledList.Item label="Protolathe">
|
|
<Button icon="unlink" onClick={() => act('disconnect', { disconnect: 'lathe' })}>
|
|
Disconnect
|
|
</Button>
|
|
</LabeledList.Item>
|
|
)) ||
|
|
null}
|
|
{(linked_imprinter.present && (
|
|
<LabeledList.Item label="Circuit Imprinter">
|
|
<Button icon="unlink" onClick={() => act('disconnect', { disconnect: 'imprinter' })}>
|
|
Disconnect
|
|
</Button>
|
|
</LabeledList.Item>
|
|
)) ||
|
|
null}
|
|
</LabeledList>
|
|
</Box>
|
|
)) || <Box>Error</Box>}
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const menus = [
|
|
{ name: 'Protolathe', icon: 'wrench', template: <ResearchConsoleConstructor name="Protolathe" /> },
|
|
{
|
|
name: 'Circuit Imprinter',
|
|
icon: 'digital-tachograph',
|
|
template: <ResearchConsoleConstructor name="Circuit Imprinter" />,
|
|
},
|
|
{ name: 'Destructive Analyzer', icon: 'eraser', template: <ResearchConsoleDestructiveAnalyzer /> },
|
|
{ name: 'Settings', icon: 'cog', template: <ResearchConsoleSettings /> },
|
|
{ name: 'Research List', icon: 'flask', template: <ResearchConsoleViewResearch /> },
|
|
{ name: 'Design List', icon: 'file', template: <ResearchConsoleViewDesigns /> },
|
|
{ name: 'Disk Operations', icon: 'save', template: <ResearchConsoleDisk /> },
|
|
];
|
|
|
|
export const ResearchConsole = (props, context) => {
|
|
const { act, data } = useBackend(context);
|
|
|
|
const { busy_msg, locked } = data;
|
|
|
|
const [menu, setMenu] = useSharedState(context, 'rdmenu', 0);
|
|
|
|
let allTabsDisabled = false;
|
|
if (busy_msg || locked) {
|
|
allTabsDisabled = true;
|
|
}
|
|
|
|
return (
|
|
<Window width={850} height={630}>
|
|
<Window.Content scrollable>
|
|
<Tabs>
|
|
{menus.map((obj, i) => (
|
|
<Tabs.Tab
|
|
key={i}
|
|
icon={obj.icon}
|
|
selected={menu === i}
|
|
disabled={allTabsDisabled}
|
|
onClick={() => setMenu(i)}>
|
|
{obj.name}
|
|
</Tabs.Tab>
|
|
))}
|
|
</Tabs>
|
|
{(busy_msg && <Section title="Processing...">{busy_msg}</Section>) ||
|
|
(locked && (
|
|
<Section title="Console Locked">
|
|
<Button onClick={() => act('lock')} icon="lock-open">
|
|
Unlock
|
|
</Button>
|
|
</Section>
|
|
)) ||
|
|
menus[menu].template}
|
|
</Window.Content>
|
|
</Window>
|
|
);
|
|
};
|